package org.jeecg.modules.online.cgform.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtil;
import org.hibernate.HibernateException;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLowApp;
import org.jeecg.common.bpm.api.IBpmBaseExtAPI;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.constant.enums.CgformEnum;
import org.jeecg.common.constant.enums.LowAppAopEnum;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.*;
import org.jeecg.modules.online.auth.entity.OnlAuthData;
import org.jeecg.modules.online.auth.entity.OnlAuthPage;
import org.jeecg.modules.online.auth.entity.OnlAuthRelation;
import org.jeecg.modules.online.auth.service.IOnlAuthDataService;
import org.jeecg.modules.online.auth.service.IOnlAuthPageService;
import org.jeecg.modules.online.auth.service.IOnlAuthRelationService;
import org.jeecg.modules.online.cgform.constant.ExtendJsonKey;
import org.jeecg.modules.online.cgform.enhance.CgformEnhanceJavaInter;
import org.jeecg.modules.online.cgform.enhance.CgformEnhanceJavaListInter;
import org.jeecg.modules.online.cgform.enhance.impl.http.CgformEnhanceHttpFormImpl;
import org.jeecg.modules.online.cgform.enhance.impl.http.CgformEnhanceHttpListImpl;
import org.jeecg.modules.online.cgform.entity.*;
import org.jeecg.modules.online.cgform.enums.CgformConstant;
import org.jeecg.modules.online.cgform.mapper.*;
import org.jeecg.modules.online.cgform.model.OnlCgformModel;
import org.jeecg.modules.online.cgform.model.OnlGenerateModel;
import org.jeecg.modules.online.cgform.service.IOnlCgformFieldService;
import org.jeecg.modules.online.cgform.service.IOnlCgformHeadService;
import org.jeecg.modules.online.cgform.service.IOnlCgformIndexService;
import org.jeecg.modules.online.cgform.util.CgformUtil;
import org.jeecg.modules.online.config.exception.BusinessException;
import org.jeecg.modules.online.config.exception.DBException;
import org.jeecg.modules.online.config.model.CgformConfigModel;
import org.jeecg.modules.online.config.model.DataBaseConfig;
import org.jeecg.modules.online.config.model.DmDataBaseConfig;
import org.jeecg.modules.online.config.service.DbTableHandleI;
import org.jeecg.modules.online.config.util.DataBaseConst;
import org.jeecg.modules.online.config.util.DbTableProcess;
import org.jeecg.modules.online.config.util.DbTableUtil;
import org.jeecgframework.codegenerate.database.DbReadTableUtil;
import org.jeecgframework.codegenerate.generate.impl.CodeGenerateOne;
import org.jeecgframework.codegenerate.generate.impl.CodeGenerateOneToMany;
import org.jeecgframework.codegenerate.generate.pojo.ColumnVo;
import org.jeecgframework.codegenerate.generate.pojo.TableVo;
import org.jeecgframework.codegenerate.generate.pojo.onetomany.MainTableVo;
import org.jeecgframework.codegenerate.generate.pojo.onetomany.SubTableVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.net.URLDecoder;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: Online表单开发
 * @Author: jeecg-boot
 * @Date: 2019-03-12
 * @Version: V1.0
 */
@Slf4j
@Service("onlCgformHeadServiceImpl")
public class OnlCgformHeadServiceImpl extends ServiceImpl<OnlCgformHeadMapper, OnlCgformHead> implements IOnlCgformHeadService {

	@Autowired
	private IOnlCgformFieldService fieldService;
	@Autowired
	private IOnlCgformIndexService indexService;
	@Autowired
	private OnlCgformEnhanceJsMapper onlCgformEnhanceJsMapper;
	@Autowired
	private OnlCgformButtonMapper onlCgformButtonMapper;
	@Autowired
	private OnlCgformEnhanceJavaMapper onlCgformEnhanceJavaMapper;
	@Autowired
	private OnlCgformEnhanceSqlMapper onlCgformEnhanceSqlMapper;
	@Autowired
	private IOnlCgformFieldService onlCgformFieldService;
	@Autowired
	private DataBaseConfig dataBaseConfig;
	@Autowired
	private IOnlAuthPageService onlAuthPageService;
	@Autowired
	private IOnlAuthDataService onlAuthDataService;
	@Autowired
	private IOnlAuthRelationService onlAuthRelationService;

	@Autowired
	@Lazy
	private IBpmBaseExtAPI bpmBaseExtAPI;
	@Autowired
	private CgformEnhanceHttpFormImpl cgformEnhanceJavaHttp;
	@Autowired
	private CgformEnhanceHttpListImpl cgformEnhanceJavaListHttp;

	@Value(value="${jeecg.online.datasource:}")
	private String onlineDatasource;

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Result<?> addAll(OnlCgformModel model) {

		String uuid = UUID.randomUUID().toString().replace("-", "");

		OnlCgformHead head = model.getHead();
		List<OnlCgformField> fields = model.getFields();
		List<OnlCgformIndex> indexs = model.getIndexs();

		head.setId(uuid);

		// 是否有外键配置
		boolean hasForeignKey = false;
		for (int i = 0; i < fields.size(); i++) {
			OnlCgformField field = fields.get(i);
			field.setId(null);
			field.setCgformHeadId(uuid);
			if (field.getOrderNum() == null) {
				field.setOrderNum(i);
			}
			if(org.apache.commons.lang.StringUtil.isNotBlank(field.getMainTable()) && StringUtil.isNotBlank(field.getMainField())){
				hasForeignKey = true;
			}
			specialFieldHandler(field);
		}

		for (OnlCgformIndex index : indexs) {
			index.setId(null);
			index.setCgformHeadId(uuid);
			index.setIsDbSynch("N");
			index.setDelFlag(CommonConstant.DEL_FLAG_0);
		}
		
		//update-begin-author:每个数据库设置的默认值不一样 导致数据显示异常,此处java代码写死
		head.setIsDbSynch("N");
		head.setQueryMode(CgformUtil.QUERY_MODE_SINGLE);
		head.setTableVersion(1);
		//update-end-author:每个数据库设置的默认值不一样 导致数据显示异常,此处java代码写死

		head.setCopyType(0);
		//update-begin-author:taoyan date:20190923 for:如果不设置排序序号 默认设置为1 issue #524
		if(head.getTableType()==3 && head.getTabOrderNum()==null) {
			head.setTabOrderNum(1);
		}
		//update-end-author:taoyan date:20190923 for:如果不设置排序序号 默认设置为1 issue #524
		super.save(head);
		fieldService.saveBatch(fields);
		indexService.saveBatch(indexs);
		
		//添加一张表 需要处理是否有主表信息
		handleMainTableInfo(head, fields);
		//update-begin-author:taoyan date:20220118 for: JTC-503 新增online子表需要清空缓存
		if(head.getTableType()==3 && hasForeignKey){
			//如果是从表 且 配置外键了
			onlCgformFieldService.clearCacheOnlineConfig();
		}
		//update-end-author:taoyan date:20220118 for: JTC-503 新增online子表需要清空缓存
		return Result.ok("添加成功");
	}

	

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Result<?> editAll(OnlCgformModel model) {
		OnlCgformHead head = model.getHead();

		OnlCgformHead headEntity = super.getById(head.getId());
		if (headEntity == null) {
			return Result.error("未找到对应实体");
		}
		String isDbSync = headEntity.getIsDbSynch();
		if(CgformUtil.databaseTableIsChange(headEntity, head)) {
			isDbSync = "N";
		}
		// table version 自增1
		Integer version = headEntity.getTableVersion();
		if (version == null) {
			version = 1;
		}
		head.setTableVersion(++version);

		List<OnlCgformField> fields = model.getFields();
		List<OnlCgformIndex> indexs = model.getIndexs();

		// 将fields分类成 添加和修改 两个列表
		List<OnlCgformField> addFields = new ArrayList<OnlCgformField>();
		List<OnlCgformField> editFields = new ArrayList<OnlCgformField>();
		for (OnlCgformField field : fields) {
			String id = String.valueOf(field.getId());
			specialFieldHandler(field);
			if (id.length() == 32) {
				// 修改项
				editFields.add(field);
			} else {
				// id 不做修改
				String primaryKey = "_pk";
				if (!primaryKey.equals(id)) {
					// 新增项
					field.setId(null);
					field.setCgformHeadId(head.getId());
					addFields.add(field);
				}
			}
		}
		if(addFields.size()>0) {
			isDbSync = "N";
		}
		int maxOrderNum = 0;
		// 批量修改
		for (OnlCgformField field : editFields) {
			OnlCgformField dbField = this.fieldService.getById(field.getId());
			// 重置 主表的 subtablestr
			resetMainTableField(dbField.getMainTable(), head.getTableName());
			boolean ischange = CgformUtil.databaseFieldIsChange(dbField, field);
			if(ischange) {
				isDbSync = "N";
			}
			if((dbField.getOrderNum()==null?0:dbField.getOrderNum())>maxOrderNum) {
				maxOrderNum = dbField.getOrderNum();
			}

			//如果字段名有变更,则记录原字段 dbFieldNameOld
			if("Y".equals(headEntity.getIsDbSynch()) && !field.getDbFieldName().equals(dbField.getDbFieldName())){
				field.setDbFieldNameOld(dbField.getDbFieldName());
			}

			// update-begin-author:taoyan date:20211019 for: 无法修改null值
			UpdateWrapper<OnlCgformField> wrapper = new UpdateWrapper<>();
			wrapper.lambda().eq(OnlCgformField::getId, field.getId());
			if(field.getFieldValidType() == null){
				// update-begin-author:taoyan date:20211111 for: oracle下设置为null 报错
				wrapper.lambda().set(OnlCgformField::getFieldValidType, "");
				// update-end-author:taoyan date:20211111 for: oracle下设置为null 报错
			}
			fieldService.update(field, wrapper);
			// update-end-author:taoyan date:20211019 for: 无法修改null值
		}
		//fieldService.saveBatch(addFields);
		for (OnlCgformField  onlCgformField: addFields) {
			if (onlCgformField.getOrderNum() == null) {
				onlCgformField.setOrderNum(++maxOrderNum);
			}
			fieldService.save(onlCgformField);
		}

		// 将item Index 分类
		List<OnlCgformIndex> dbCgformIndex = indexService.getCgformIndexsByCgformId(head.getId());
		List<OnlCgformIndex> addIndex = new ArrayList<OnlCgformIndex>();
		List<OnlCgformIndex> editIndex = new ArrayList<OnlCgformIndex>();
		for (OnlCgformIndex index : indexs) {
			String id = String.valueOf(index.getId());
			if (id.length() == 32) {
				// 修改项
				editIndex.add(index);
			} else {
				// 新增项
				index.setId(null);
				index.setIsDbSynch("N");
				index.setDelFlag(CommonConstant.DEL_FLAG_0);
				index.setCgformHeadId(head.getId());
				addIndex.add(index);
			}
		}

		//删除索引配置(通过逻辑删除字段记录删除状态，方便同步数据库的时候删除索引)
		for (OnlCgformIndex index : dbCgformIndex) {
			boolean bool = indexs.stream().anyMatch(a -> index.getId().equals(a.getId()));
			if(!bool){
				index.setDelFlag(CommonConstant.DEL_FLAG_1);
				editIndex.add(index);
				isDbSync = "N";
			}
		}

		if(addIndex.size()>0) {
			isDbSync = "N";
			indexService.saveBatch(addIndex);
		}

		for (OnlCgformIndex index : editIndex) {
			OnlCgformIndex dbIndex = this.indexService.getById(index.getId());
			boolean ischange = CgformUtil.databaseIndexIsChange(dbIndex, index);
			if(ischange) {
				isDbSync = "N";
				index.setIsDbSynch("N");
			}
			indexService.updateById(index);
		}

		// 删除项
		if (model.getDeleteFieldIds().size() > 0) {
			isDbSync = "N";
			List<String> deleteFieldIdList = model.getDeleteFieldIds();
			//update-begin-author:taoyan date:20210528 for: 删除外键对应的字段 主表上的subtablestr未重置
			for(String deleteId: deleteFieldIdList){
				OnlCgformField deleteField = fieldService.getById(deleteId);
				if(deleteField!=null){
					resetMainTableField(deleteField.getMainTable(), head.getTableName());
					fieldService.removeById(deleteId);
				}
			}
			//update-end-author:taoyan date:20210528 for: 删除外键对应的字段 主表上的subtablestr未重置
		}
//		采用del_flag标记删除，不直接物理删除数据
//		if (model.getDeleteIndexIds().size() > 0) {
//			isDbSync = "N";
//			indexService.removeByIds(model.getDeleteIndexIds());
//		}

		head.setIsDbSynch(isDbSync);
		super.updateById(head);
		
		handleMainTableInfo(head, fields);
		
		handleCopyTableColumns(head,fields);
		
		return Result.ok("全部修改成功");
	}

	/**
	 * 将主表对应的 subTableStr字段 去掉当前表名
	 * @param mainTableName
	 * @param currentTableName
	 */
	private void resetMainTableField(String mainTableName, String currentTableName){
		if(StringUtil.isNotBlank(mainTableName)){
			// 如果原来有外键配置 需要将外键对应的主表信息删除
			LambdaQueryWrapper mainTableQuery = new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, mainTableName);
			OnlCgformHead mainTable = this.baseMapper.selectOne(mainTableQuery);
			// issues/I4GG21 Online在线表单设置外键保存失败问题
			if(mainTable!=null && StringUtil.isNotBlank(mainTable.getSubTableStr())){
				String subTableString = mainTable.getSubTableStr();
				String[] arr = subTableString.split(",");
				List<String> ls = new ArrayList<>();
				for(String a: arr){
					if(a.equals(currentTableName)){
						continue;
					}
					ls.add(a);
				}
				mainTable.setSubTableStr(String.join(",", ls));
				this.baseMapper.updateById(mainTable);
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void doDbSynch(String code,String synMethod) throws HibernateException, IOException, TemplateException, SQLException, DBException {
		//1.判断实体配置
		OnlCgformHead onlCgformHead = this.getById(code);
		if(onlCgformHead==null) {
			throw new DBException("实体配置不存在");
		}
		String tbname = onlCgformHead.getTableName();
		//2.查询字段集合
		LambdaQueryWrapper<OnlCgformField> query = new LambdaQueryWrapper<OnlCgformField>();
		query.eq(OnlCgformField::getCgformHeadId, code);
		query.orderByAsc(OnlCgformField::getOrderNum);
		
		List<OnlCgformField> fieldList =  fieldService.list(query);
		//3.构造同步参数CgformConfigModel实例
		CgformConfigModel model = new CgformConfigModel();
		model.setTableName(tbname);
		model.setJformPkType(onlCgformHead.getIdType());
		model.setJformPkSequence(onlCgformHead.getIdSequence());
		model.setContent(onlCgformHead.getTableTxt());
		model.setColumns(fieldList);
		//获取DB配置
		DataBaseConfig dbc = this.getOnlineDataBaseConfig();
		model.setDbConfig(dbc);
		DbType dbTypeEnum = DbTableUtil.getDbType(dbc);
		//4.判断是否普通同步
		//update-begin-author:taoyan date:sqlite 不支持修改表 只能创建表、删除原表
		if (CgformUtil.SYNC_NORMAL.equals(synMethod) && !dbTypeEnum.equals(DbType.SQLITE)) {
			long startTime = System.currentTimeMillis();
			boolean isExist = DbTableUtil.judgeTableIsExit(tbname, dbc);
			//log.info("==判断表是否存在消耗时间"+ (System.currentTimeMillis()-startTime)+"毫秒");
			//判断数据库表是否存在 
			if(isExist) {
				//step.1 表字段变更处理
				DbTableProcess process = new DbTableProcess(dbc);
				List<String> list = process.updateTable(model);
				for (String sql : list) {
					if(oConvertUtils.isEmpty(sql)||oConvertUtils.isEmpty(sql.trim())) {
						continue;
					}
					//update-begin-author:taoyan date:20211228 for: 有时候sql里面有多条语句 这与druid的wall冲突报错 sql injection violation, multi-statement not allow
					String[] sqlArr = sql.split(";");
                    if (sqlArr != null && sqlArr.length > 1) {
                        //log.info(" executeDDL all sql: {} ", sql);
                    }

					for(String singleSql: sqlArr){
						if(oConvertUtils.isEmpty(singleSql)||oConvertUtils.isEmpty(singleSql.trim())) {
							continue;
						}
						//log.info(" executeDDL single sql : {}",singleSql);
						this.baseMapper.executeDDL(singleSql);
					}
					//update-end-author:taoyan date:20211228 for: 有时候sql里面有多条语句 这与druid的wall冲突报错 sql injection violation, multi-statement not allow
				}

				//===========================================================================================================================================
				//step.2 表索引变更处理
				//表已存在 那么就先删除索引
				List<OnlCgformIndex> configIndexList = indexService.list(new LambdaQueryWrapper<OnlCgformIndex>().eq(OnlCgformIndex::getCgformHeadId, code));
				//update-begin-author:taoyan date:20190826 for：不能删除不存在的索引 ，删除前判断一下
				for (OnlCgformIndex configIndex : configIndexList) {
					if("N".equals(configIndex.getIsDbSynch()) || CommonConstant.DEL_FLAG_1.equals(configIndex.getDelFlag())){
						String countIndexSql = process.getCountIndexSql(configIndex.getIndexName(), tbname);
						if(indexService.isExistIndex(countIndexSql)){
							String delIndexSql = process.dropIndex(configIndex.getIndexName(), tbname);
							try {
								//log.info("删除索引 executeDDL:"+ delIndexSql);
								this.baseMapper.executeDDL(delIndexSql);
								//删除索引后，将逻辑删除数据，从物理表里删除
								if(CommonConstant.DEL_FLAG_1.equals(configIndex.getDelFlag())){
									indexService.removeById(configIndex.getId());
								}
							} catch (Exception e) {
								log.error("删除表【"+tbname+"】索引("+configIndex.getIndexName()+")失败!",e);
							}
						}else{
							//库中无索引，则将逻辑删除数据，从物理表里删除
							if(CommonConstant.DEL_FLAG_1.equals(configIndex.getDelFlag())){
								indexService.removeById(configIndex.getId());
							}
						}
					}
				}
				//update-end-author:taoyan date:20190826 for：不能删除不存在的索引 ，删除前判断一下
				//===========================================================================================================================================
			}else {
				DbTableProcess.createTable(model);
			}
		}else if (CgformUtil.SYNC_FORCE.equals(synMethod) || dbTypeEnum.equals(DbType.SQLITE)) {
			// TODO sqlite修改表也走该逻辑 需要先修改表名 然后创建表 然后复制数据 关键在于老表字段获取和新表字段的对应 拼接 insert select
			//强制同步，先删除后添加
			DbTableHandleI handler = DbTableUtil.getTableHandle();
			String sql = handler.dropTableSQL(tbname);
			this.baseMapper.executeDDL(sql);
			DbTableProcess.createTable(model);
		}
		
		//处理完表 再处理索引
		indexService.createIndex(code, DbTableUtil.getDatabaseType(),tbname);
		
		//同步完成 修改同步标识
		onlCgformHead.setIsDbSynch("Y");
		//update-begin-author:taoyan date:20201211 for:如果该表未曾同步/修改过（这个状态下可以修改表名） 那么此为第一次同步 设置版本号为2
		if(onlCgformHead.getTableVersion()==1){
			onlCgformHead.setTableVersion(2);
		}
		//update-end-author:taoyan date:20201211 for:如果该表未曾同步/修改过（这个状态下可以修改表名） 那么此为第一次同步 设置版本号为2
		this.updateById(onlCgformHead);
	}

	@Override
	public void deleteRecordAndTable(String id) throws DBException, SQLException {
		OnlCgformHead onlCgformHead = this.getById(id);
		if(onlCgformHead==null) {
			throw new DBException("实体配置不存在");
		}
		long startTime = System.currentTimeMillis();
		boolean isExist = DbTableUtil.judgeTableIsExit(onlCgformHead.getTableName());
		//log.info("==判断表是否存在消耗时间 "+ (System.currentTimeMillis()-startTime)+"毫秒");
		if(isExist){
			String sql = DbTableUtil.getTableHandle().dropTableSQL(onlCgformHead.getTableName());
			//log.info(" 删除表  executeDDL： "+ sql);
			this.baseMapper.executeDDL(sql);
		}
		deleteRecord(id);
	}

	@Override
	public void deleteRecord(String id) throws DBException, SQLException {
		OnlCgformHead onlCgformHead = this.getById(id);
		if(onlCgformHead==null) {
			throw new DBException("实体配置不存在");
		}
		//update-begin-author:taoyan date:20210909 for:【Online表单开发】移除表单，没有删除关联表数据 #2988
		// 查询视图表配置
		LambdaQueryWrapper<OnlCgformHead> query = new LambdaQueryWrapper<>();
		query.eq(OnlCgformHead::getPhysicId, id);
		List<OnlCgformHead> ls = this.baseMapper.selectList(query);
		if(ls!=null && ls.size()>0){
			for(OnlCgformHead head: ls){
				removeOnlineTableConfig(head.getId());
			}
		}
		//update-end-author:taoyan date:20210909 for:【Online表单开发】移除表单，没有删除关联表数据 #2988
		// 如果是子表 需要考虑主表的subTableStr字段
		this.deleteSubTableConfig(onlCgformHead);
		//移除当前表的配置
		removeOnlineTableConfig(id);
		//update-begin-author:taoyan date:20220118 for: JTC-503 移除online子表需要清空缓存
		if(onlCgformHead.getTableType()==3){
			//如果是从表 删除缓存
			onlCgformFieldService.clearCacheOnlineConfig();
		}
		//update-end-author:taoyan date:20220118 for: JTC-503 移除online子表需要清空缓存
	}

	//update-begin-author:taoyan date:20210909 for:【Online表单开发】移除表单，没有删除关联表数据 #2988
	private void removeOnlineTableConfig(String id){
		// 删除 onl_cgform_head
		this.baseMapper.deleteById(id);
		//删除 onl_cgform_field
		LambdaQueryWrapper<OnlCgformField> fieldQuery = new LambdaQueryWrapper<>();
		fieldQuery.eq(OnlCgformField::getCgformHeadId, id);
		this.fieldService.remove(fieldQuery);

		//删除 onl_cgform_index
		LambdaQueryWrapper<OnlCgformIndex> indexQuery = new LambdaQueryWrapper<>();
		indexQuery.eq(OnlCgformIndex::getCgformHeadId, id);
		this.indexService.remove(indexQuery);

		//删除 OnlAuthRelation
		LambdaQueryWrapper<OnlAuthRelation> authRelationQuery = new LambdaQueryWrapper<>();
		authRelationQuery.eq(OnlAuthRelation::getCgformId, id);
		onlAuthRelationService.remove(authRelationQuery);

		//删除 OnlAuthData
		LambdaQueryWrapper<OnlAuthData> authDataQuery = new LambdaQueryWrapper<>();
		authDataQuery.eq(OnlAuthData::getCgformId, id);
		onlAuthDataService.remove(authDataQuery);

		//删除onl_auth_page
		LambdaQueryWrapper<OnlAuthPage> authQuery = new LambdaQueryWrapper<>();
		authQuery.eq(OnlAuthPage::getCgformId, id);
		onlAuthPageService.remove(authQuery);
	}
	//update-end-author:taoyan date:20210909 for:【Online表单开发】移除表单，没有删除关联表数据 #2988

	/**
	 * 删除子表 需要关联删除主表的subTableStr
	 * @param onlCgformHead
	 */
	private void deleteSubTableConfig(OnlCgformHead onlCgformHead){
		if(onlCgformHead.getTableType()==3){
			//只有附表才需要处理此处逻辑
			LambdaQueryWrapper query = new LambdaQueryWrapper<OnlCgformField>().eq(OnlCgformField::getCgformHeadId,onlCgformHead.getId());
			List<OnlCgformField> list = this.fieldService.list(query);
			String mainTableName = null;
			//1.先找到主表名称
			for (OnlCgformField field : list) {
				mainTableName = field.getMainTable();
				//update-begin--Author:scott----  Date:20201016 ----for：OL一对多 移除或删除附表后主表生成代码报错--表信息加载失败 #1773----
				if(StringUtil.isNotBlank(mainTableName)){
					break;
				}
				//update-end--Author:scott----  Date:20201016 ----for：OL一对多 移除或删除附表后主表生成代码报错--表信息加载失败 #1773----
			}
			if(StringUtil.isNotBlank(mainTableName)){
				OnlCgformHead mainTable = baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName,mainTableName));
				if(mainTable!=null){
					//2.再找到主表 SubTableStr 进行剔除
					String str = mainTable.getSubTableStr();
					if(StringUtil.isNotBlank(str)){
						List<String> strList = Arrays.asList(str.split(",")).stream().collect(Collectors.toList());
						strList.remove(onlCgformHead.getTableName());
						mainTable.setSubTableStr(String.join(",",strList));
						this.baseMapper.updateById(mainTable);
					}
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Map<String, Object>> queryListData(String sql){
		return this.baseMapper.queryList(sql);

	}

	@Override
	public void saveEnhance(OnlCgformEnhanceJs onlCgformEnhanceJs) {
		this.onlCgformEnhanceJsMapper.insert(onlCgformEnhanceJs);
	}

	@Override
	public OnlCgformEnhanceJs queryEnhance(String code,String type) {
		return this.onlCgformEnhanceJsMapper.selectOne(new LambdaQueryWrapper<OnlCgformEnhanceJs>().eq(OnlCgformEnhanceJs::getCgJsType , type).eq(OnlCgformEnhanceJs::getCgformHeadId , code));
	}

	@Override
	public void editEnhance(OnlCgformEnhanceJs onlCgformEnhanceJs) {
		this.onlCgformEnhanceJsMapper.updateById(onlCgformEnhanceJs);
		
	}

	@Override
	public OnlCgformEnhanceSql queryEnhanceSql(String formId,String buttonCode) {
		return this.onlCgformEnhanceSqlMapper.selectOne(new LambdaQueryWrapper<OnlCgformEnhanceSql>().eq(OnlCgformEnhanceSql::getCgformHeadId, formId).eq(OnlCgformEnhanceSql::getButtonCode, buttonCode));
	}

	@Override
	public OnlCgformEnhanceJava queryEnhanceJava(OnlCgformEnhanceJava onlCgformEnhanceJava) {
		LambdaQueryWrapper<OnlCgformEnhanceJava> query = new LambdaQueryWrapper<OnlCgformEnhanceJava>();
		query.eq(OnlCgformEnhanceJava::getButtonCode,onlCgformEnhanceJava.getButtonCode());
		query.eq(OnlCgformEnhanceJava::getCgformHeadId,onlCgformEnhanceJava.getCgformHeadId());
		query.eq(OnlCgformEnhanceJava::getCgJavaType,onlCgformEnhanceJava.getCgJavaType());
		query.eq(OnlCgformEnhanceJava::getEvent,onlCgformEnhanceJava.getEvent());
		return this.onlCgformEnhanceJavaMapper.selectOne(query);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<OnlCgformButton> queryButtonList(String code,boolean isListButton) {
		LambdaQueryWrapper<OnlCgformButton> query = new LambdaQueryWrapper<OnlCgformButton>();
		query.eq(OnlCgformButton::getButtonStatus, "1");
		query.eq(OnlCgformButton::getCgformHeadId, code);
		if(isListButton) {
			query.in(OnlCgformButton::getButtonStyle, "link","button");
		}else {
			query.eq(OnlCgformButton::getButtonStyle, "form");
		}
		query.orderByAsc(OnlCgformButton::getOrderNum);
		return this.onlCgformButtonMapper.selectList(query);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<OnlCgformButton> queryButtonList(String code) {
		LambdaQueryWrapper<OnlCgformButton> query = new LambdaQueryWrapper<OnlCgformButton>();
		query.eq(OnlCgformButton::getButtonStatus, "1");
		query.eq(OnlCgformButton::getCgformHeadId, code);
		query.orderByAsc(OnlCgformButton::getOrderNum);
		return this.onlCgformButtonMapper.selectList(query);
	}


	@Override
	public List<String> queryOnlinetables() {
		return this.baseMapper.queryOnlinetables();
	}

	@Override
	@AutoLowApp(action = LowAppAopEnum.CGFORM_DB_IMPORT, bizType = "cgform")
	@Transactional(rollbackFor = Exception.class)
	public void saveDbTable2Online(String tbname) {
		OnlCgformHead cgFormHead = new OnlCgformHead();
		cgFormHead.setTableType(1);
		cgFormHead.setIsCheckbox("Y");
		cgFormHead.setIsDbSynch("Y");
		cgFormHead.setIsTree("N");
		cgFormHead.setIsPage("Y");
		cgFormHead.setQueryMode("group");
		cgFormHead.setTableName(tbname.toLowerCase());
		cgFormHead.setTableTxt(tbname);//TODO 能不能获取表的备注信息
		cgFormHead.setTableVersion(1);
		cgFormHead.setFormTemplate("1");//默认一列展示
		cgFormHead.setCopyType(0);
		cgFormHead.setIsDesForm("N");
		cgFormHead.setScroll(1);//默认滚动条-有
		cgFormHead.setThemeTemplate("normal");//默认主题模板
		String cgFormId = UUIDGenerator.generate();
		cgFormHead.setId(cgFormId);
		List<OnlCgformField> columnsList = new ArrayList<OnlCgformField>();
		try {
			List<ColumnVo> list = DbReadTableUtil.readOriginalTableColumn(tbname);
			for (int k = 0; k < list.size(); k++) {
				ColumnVo columnt = list.get(k);
				//log.info("  columnt : "+ columnt.toString());
				String fieldName = columnt.getFieldDbName();
				OnlCgformField cgFormField = new OnlCgformField();
				cgFormField.setCgformHeadId(cgFormId);
				cgFormField.setDbFieldNameOld(columnt.getFieldDbName().toLowerCase());
				cgFormField.setDbFieldName(columnt.getFieldDbName().toLowerCase());
						
				if (StringUtil.isNotBlank(columnt.getFiledComment())) {
					cgFormField.setDbFieldTxt(columnt.getFiledComment());
				} else {
					cgFormField.setDbFieldTxt(columnt.getFieldName());
				}
				
				cgFormField.setDbIsKey(0);
				cgFormField.setIsShowForm(1);
				cgFormField.setIsQuery(0);
				cgFormField.setFieldMustInput("0");
				cgFormField.setIsShowList(1);
				cgFormField.setOrderNum(k + 1);
				cgFormField.setQueryMode("single");
				cgFormField.setDbLength(oConvertUtils.getInt(columnt.getPrecision()));
				cgFormField.setFieldLength(120);
				cgFormField.setDbPointLength(oConvertUtils.getInt(columnt.getScale()));
				cgFormField.setFieldShowType("text");
				cgFormField.setDbIsNull("Y".equals(columnt.getNullable())?1:0);
				cgFormField.setIsReadOnly(0);
				
				if("id".equalsIgnoreCase(fieldName)){
					String[] pkTypeArr = {"java.lang.Integer","java.lang.Long"};
					String idFiledType = columnt.getFieldType();
					if(Arrays.asList(pkTypeArr).contains(idFiledType)){
						//如果主键是数字类型,则设置为自增长
						cgFormHead.setIdType("NATIVE");
					}else{
						//否则设置为UUID
						cgFormHead.setIdType("UUID");
					}
					cgFormField.setDbIsKey(1);
					cgFormField.setIsShowForm(0);
					cgFormField.setIsShowList(0);
					cgFormField.setIsReadOnly(1);
				}
				//update-begin--Author:lvdandan----  Date:20201211 ----for：【online】导入表单，标准字段怎么成显示状态
				//标准字段默认列表及表单不显示
				if("create_by".equalsIgnoreCase(fieldName) || "create_time".equalsIgnoreCase(fieldName) || "update_by".equalsIgnoreCase(fieldName)
					|| "update_time".equalsIgnoreCase(fieldName) || "sys_org_code".equalsIgnoreCase(fieldName)){
					cgFormField.setIsShowForm(0);
					cgFormField.setIsShowList(0);
				}
				//update-end--Author:lvdandan----  Date:20201211 ----for：【online】导入表单，标准字段怎么成显示状态
				if ("java.lang.Integer".equalsIgnoreCase(columnt.getFieldType())){
					cgFormField.setDbType(DataBaseConst.INT);
				}else if ("java.lang.Long".equalsIgnoreCase(columnt.getFieldType())) {
					cgFormField.setDbType(DataBaseConst.INT);
				} else if ("java.util.Date".equalsIgnoreCase(columnt.getFieldType())) {
					cgFormField.setDbType(DataBaseConst.DATE);
					cgFormField.setFieldShowType("date");
				} else if ("java.lang.Double".equalsIgnoreCase(columnt.getFieldType())
						||"java.lang.Float".equalsIgnoreCase(columnt.getFieldType())) {
					cgFormField.setDbType(DataBaseConst.DOUBLE);
				} else if ("java.math.BigDecimal".equalsIgnoreCase(columnt.getFieldType()) || "BigDecimal".equalsIgnoreCase(columnt.getFieldType())) {
					cgFormField.setDbType(DataBaseConst.BIGDECIMAL);
				} else if ("byte[]".equalsIgnoreCase(columnt.getFieldType()) || columnt.getFieldType().contains("blob")) {
					cgFormField.setDbType(DataBaseConst.BLOB);
					columnt.setCharmaxLength(null);
                    //update-begin--Author:scott----  Date:20190924 ----for：Postgresql数据库导入表单,字段Text类型匹配问题解决----
				} else if ("java.lang.Object".equals(columnt.getFieldType()) && ("text".equalsIgnoreCase(columnt.getFieldDbType()) ||"ntext".equalsIgnoreCase(columnt.getFieldDbType()) ) ) {
					cgFormField.setDbType(DataBaseConst.TEXT);
					cgFormField.setFieldShowType(DataBaseConst.TEXTAREA);
                    //update-end--Author:scott----  Date:20190924 ----for：Postgresql数据库导入表单,字段Text类型匹配问题解决----
				}else if ("java.lang.Object".equals(columnt.getFieldType()) && "image".equalsIgnoreCase(columnt.getFieldDbType())) {
					cgFormField.setDbType(DataBaseConst.BLOB);
					//update-end--Author:scott----  Date:20190924 ----for：Postgresql数据库导入表单,字段Text类型匹配问题解决----
				} else {
					cgFormField.setDbType(DataBaseConst.STRING);
				}
				if (oConvertUtils.isEmpty(columnt.getPrecision()) && StringUtil.isNotBlank(columnt.getCharmaxLength())) {
					if (Long.valueOf(columnt.getCharmaxLength()) >= 3000) {
						cgFormField.setDbType(DataBaseConst.TEXT);
						cgFormField.setFieldShowType(DataBaseConst.TEXTAREA);
						try{//有可能长度超出int的长度
							cgFormField.setDbLength(Integer.valueOf(columnt.getCharmaxLength()));
						}catch(Exception e){
							log.error(e.getMessage(), e);
						}
					} else {
						cgFormField.setDbLength(Integer.valueOf(columnt.getCharmaxLength()));
					}
				} else {
					if (StringUtil.isNotBlank(columnt.getPrecision())) {
						cgFormField.setDbLength(Integer.valueOf(columnt.getPrecision()));
					}
					else{
						if(cgFormField.getDbType().equals(DataBaseConst.INT)){
							cgFormField.setDbLength(10);
						}
					}
					if (StringUtil.isNotBlank(columnt.getScale())) {
						cgFormField.setDbPointLength(Integer.valueOf(columnt.getScale()));
					}

				}

				//update-begin--Author:scott----  Date:20200416 ----for：sqlserver数据库导入表单,字段nvarchar(max)类型db长度-1问题----
				if(oConvertUtils.getInt(columnt.getPrecision()) ==-1 && oConvertUtils.getInt(columnt.getScale())==0){
					cgFormField.setDbType(DataBaseConst.TEXT);
				}
				//update-end--Author:scott----  Date:20200416 ----for：sqlserver数据库导入表单,字段nvarchar(max)类型db长度-1问题-----
				//update-begin--Author:scott----  Date:20200416 ----for：大字段长度和精度统一设置为零----
				if(DataBaseConst.BLOB.equals(cgFormField.getDbType()) || DataBaseConst.TEXT.equals(cgFormField.getDbType()) || DataBaseConst.DATE.equals(cgFormField.getDbType())){
					cgFormField.setDbLength(0);
					cgFormField.setDbPointLength(0);
				}
				//update-end--Author:scott----  Date:20200416 ----for：大字段长度和精度统一设置为零----

				columnsList.add(cgFormField);
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		if(oConvertUtils.isEmpty(cgFormHead.getFormCategory())){
			cgFormHead.setFormCategory("bdfl_include");
		}
		
		//保存表 字段 索引信息 
		//TODO 话说索引信息去哪里了
		this.save(cgFormHead);
		this.fieldService.saveBatch(columnsList);
		
	}

	/**
	 * 判断当前表是不是在主表的 子表字符串字段中
	 * @param tableName
	 * @param subTableStr
	 * @return
	 */
	private boolean isExistSubTable(String tableName, String subTableStr){
		if(oConvertUtils.isEmpty(subTableStr)){
			return false;
		}
		String[] subTableNameArray = subTableStr.split(",");
		for(String temp: subTableNameArray){
			if(temp.equalsIgnoreCase(tableName)){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 若当前为子表 需要同步主表的SubTableStr字段
	 * @param head
	 * @param fields
	 */
	private void handleMainTableInfo(OnlCgformHead head, List<OnlCgformField> fields) {
		//1.判断子表类型
		if(head.getTableType()==3) {
			head = this.baseMapper.selectById(head.getId());
			for (int i = 0; i < fields.size(); i++) {
				//2.遍历子表字段 拿到字段的外键配置
				OnlCgformField field = fields.get(i);
				String tbName = field.getMainTable();
				if(oConvertUtils.isEmpty(tbName)) {
					continue;
				}
				//3.拿到外键表名 获取主表信息
				OnlCgformHead mainTable = this.baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, tbName));
				if(mainTable==null) {
					continue;
				}
				
				//4.拿到主表SubTableStr 和当前这个做排序楼
				String allSubTable = mainTable.getSubTableStr();
				if(oConvertUtils.isEmpty(allSubTable)) {
					allSubTable = head.getTableName();
				}else {
					//TODO 这边应该对所有子表做一个排序
					if(isExistSubTable(head.getTableName(), allSubTable)) {
						//如果当前这个子表字符串 包含当前子表 直接无视
					}else {
						//否则遍历、排序、加入
						@SuppressWarnings("unchecked")
						List<String> arr = new ArrayList(Arrays.asList(allSubTable.split(",")));
						for(int k=0;k<arr.size();k++) {
							String tempTbname = arr.get(k);
							OnlCgformHead tempTable = this.baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, tempTbname));
							if(tempTable==null) {
								continue;
							}
							if(head.getTabOrderNum()<oConvertUtils.getInt(tempTable.getTabOrderNum(),0)) {
								arr.add(k, head.getTableName());
								break;
							}
						}
						if(arr.indexOf(head.getTableName())<0) {
							arr.add(head.getTableName());
						}
						allSubTable = String.join(",", arr);
					}
				}
				mainTable.setSubTableStr(allSubTable);
				this.baseMapper.updateById(mainTable);
				break;
			}
		}else{
            //update-begin--Author:scott----  Date:20190925 ----for：TASK #3191 【online表单】一对多，附表改成单表，去掉关联后，主表sub_table_str字段还存在附表名---
            //由附表变为普通表单情况，判断是否在主表中存在附表配置，有的话则去掉该
            List<OnlCgformHead> mainTableList = this.baseMapper.selectList(new LambdaQueryWrapper<OnlCgformHead>().like(OnlCgformHead::getSubTableStr, head.getTableName()));
            if (mainTableList != null && mainTableList.size() > 0) {
                for (OnlCgformHead h : mainTableList) {
                    String subTableStr = h.getSubTableStr();
                    if (h.getSubTableStr().equals(head.getTableName())) {
                        subTableStr = "";
                    } else if (h.getSubTableStr().startsWith(head.getTableName() + ",")) {
                        subTableStr = subTableStr.replace(head.getTableName() + ",", "");
                    } else if (h.getSubTableStr().endsWith("," + head.getTableName())) {
                        subTableStr = subTableStr.replace("," + head.getTableName(), "");
                    } else if (h.getSubTableStr().indexOf("," + head.getTableName() + ",") != -1) {
                        subTableStr = subTableStr.replace("," + head.getTableName() + ",", ",");
                    }
                    h.setSubTableStr(subTableStr);
                    this.baseMapper.updateById(h);
                }
            }
            //update-end--Author:scott----  Date:20190925 ----for：TASK #3191 【online表单】一对多，附表改成单表，去掉关联后，主表sub_table_str字段还存在附表名---
        }
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public String saveManyFormData(String code, JSONObject json,String xAccessToken) throws DBException, BusinessException {
		OnlCgformHead head = this.getById(code);
		if (head == null) {
			throw new DBException("数据库主表ID["+code+"]不存在");
		}
		//执行java增强 事件类型start
		String currButtonCode = "add";
		this.executeEnhanceJava(currButtonCode,"start", head, json);
		
		//json.put("id", UUIDGenerator.generate());
		String tbname = CgformUtil.getRealTableName(head.getTableName());
		if(head.getTableType()==2) {
			//如果他是主表 则还需要保存子表的数据
			String subTables = head.getSubTableStr();
			// 判断一下防止空指针
			if(StringUtil.isNotBlank(subTables)){
				String[] arr = subTables.split(",");
				for (String tb : arr) {

					JSONArray jsonArray = json.getJSONArray(tb);
					if(jsonArray==null || jsonArray.size()==0) {
						continue;
					}

					OnlCgformHead temp = this.baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, tb));
					if(temp==null) {
						continue;
					}

					List<OnlCgformField> subFieldList = this.fieldService.list(new LambdaQueryWrapper<OnlCgformField>().eq(OnlCgformField::getCgformHeadId, temp.getId()));
					//这里有一步非常重要 需要获取子表字段对应设置外键的那个字段然后赋值
					String sublinkField="",subLinkValue=null;
					for (OnlCgformField field : subFieldList) {
						if(oConvertUtils.isEmpty(field.getMainField())) {
							continue;
						}
						sublinkField = field.getDbFieldName();
						String mainField = field.getMainField();

						//update-begin-author:taoyan date:20190924 for:主子表关联字段取值bug
						if(json.get(mainField.toLowerCase())!=null){
							subLinkValue = json.getString(mainField.toLowerCase());
						}
						if(json.get(mainField.toUpperCase())!=null){
							subLinkValue = json.getString(mainField.toUpperCase());
						}
						//update-end-author:taoyan date:20190924 for:主子表关联字段取值bug
					}

					for(int i=0;i<jsonArray.size();i++) {
						JSONObject jsonObject = jsonArray.getJSONObject(i);
						if(subLinkValue!=null) {
							jsonObject.put(sublinkField, subLinkValue);//设置外键值
						}
						this.fieldService.saveFormData(subFieldList, tb, jsonObject);
					}
				}
			}

		}
		
		//如果是tree则 新增时 赋值：是否子节点
		if("Y".equals(head.getIsTree())) {
			this.fieldService.saveTreeFormData(code, tbname, json, head.getTreeIdField(), head.getTreeParentIdField());
		}else {
			this.fieldService.saveFormData(code, tbname, json,false);
		}
		//执行sql 增强
		this.executeEnhanceSql(currButtonCode, code, json);
		//执行java 增强 事件类型end
		this.executeEnhanceJava(currButtonCode,"end", head, json);

		try {
			this.submitOnlineFlow(json, head.getTableName(),head.getId());
		} catch (Exception exception) {
			throw new JeecgBootException("流程发起失败"+exception.getMessage());
		}
		return head.getTableName();
	}

	@Override
	public Map<String, Object> querySubFormData(String table, String mainId) throws DBException {
		Map<String, Object> map = new HashMap<String, Object>();
		OnlCgformHead head = this.getOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName,table));
		if (head == null) {
			throw new DBException("数据库子表["+table+"]不存在");
		}
		List<OnlCgformField> subFieldList = this.fieldService.queryFormFields(head.getId(),false);
		String sublinkField=null;
		for (OnlCgformField field : subFieldList) {
			if(StringUtil.isNotBlank(field.getMainField())) {
				sublinkField = field.getDbFieldName();
				break;
			}
		}
		List<Map<String, Object>> subList = this.fieldService.querySubFormData(subFieldList, table, sublinkField, mainId);
		if(subList!=null && subList.size()==0) {
			throw new DBException("数据库子表["+table+"]未找到相关信息,主表ID为"+mainId);
		}else if(subList.size()>1){
			throw new DBException("数据库子表["+table+"]存在多条记录,主表ID为"+mainId);
		}else {
			map=subList.get(0);
		}
		return map;
	}

	/** 查询子表所有的数据 */
	@Override
	public List<Map<String, Object>> queryManySubFormData(String table, String mainId) throws DBException {
		OnlCgformHead head = this.getOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName,table));
		if (head == null) {
			throw new DBException("数据库子表["+table+"]不存在");
		}
		List<OnlCgformField> subFieldList = this.fieldService.queryFormFields(head.getId(),false);
		//update-begin-author:taoyan date:20210417 for: ONLINE表单 修改添加 没有字段配置的时候报错 https://gitee.com/jeecg/jeecg-boot/issues/I3HTON
		if(subFieldList==null || subFieldList.size()==0){
			throw new DBException("找不到子表字段，请确认配置是否正确!");
		}
		//update-end-author:taoyan date:20210417 for: ONLINE表单 修改添加 没有字段配置的时候报错 https://gitee.com/jeecg/jeecg-boot/issues/I3HTON
		// update-begin--Author:lvdandan Date:20200611 for：online表单-内嵌风格用其他字段为外键时，子表查询问题修改
		String sublinkField=null,mainLinkTable=null,mainLinkField=null;
		for (OnlCgformField field : subFieldList) {
			if(StringUtil.isNotBlank(field.getMainField())) {
				sublinkField = field.getDbFieldName();
				mainLinkTable = field.getMainTable();
				mainLinkField = field.getMainField();
				break;
			}
		}
		//获取外键值
		List<OnlCgformField> mainFieldList = new ArrayList<OnlCgformField>();
		OnlCgformField mainField = new OnlCgformField();
		mainField.setDbFieldName(mainLinkField);
		mainFieldList.add(mainField);
		Map<String, Object> mainMap = this.fieldService.queryFormData(mainFieldList, mainLinkTable, mainId);
		String value = oConvertUtils.getString(oConvertUtils.getString(mainMap.get(mainLinkField)),oConvertUtils.getString(mainMap.get(mainLinkField.toUpperCase())));;
		List<Map<String, Object>> subList = this.fieldService.querySubFormData(subFieldList, table, sublinkField, value);
		// update-begin--Author:lvdandan Date:20200611 for：online表单-内嵌风格用其他字段为外键时，子表查询问题修改
		if(subList!=null && subList.size()==0) {
			throw new DBException("数据库子表["+table+"]未找到相关信息,主表ID为"+mainId);
		} else {
			List<Map<String, Object>> result = new ArrayList<Map<String,Object>>(subList.size());
			for (Map<String, Object> map : subList) {
				result.add(CgformUtil.transLowerCaseMapKey(map));
			}
			return result;
		}
	}
	
	
	@Override
	public Map<String, Object> queryManyFormData(String code, String id) throws DBException {
		OnlCgformHead head = this.getById(code);
		if (head == null) {
			throw new DBException("数据库主表ID["+code+"]不存在");
		}
		List<OnlCgformField> fieldList = this.fieldService.queryFormFields(code,true);
		//update-begin-author:taoyan date:20210417 for: ONLINE表单 修改添加 没有字段配置的时候报错 https://gitee.com/jeecg/jeecg-boot/issues/I3HTON
		if(fieldList==null || fieldList.size()==0){
			throw new DBException("找不到字段，请确认配置是否正确!");
		}
		//update-end-author:taoyan date:20210417 for: ONLINE表单 修改添加 没有字段配置的时候报错 https://gitee.com/jeecg/jeecg-boot/issues/I3HTON
		Map<String, Object> map = this.fieldService.queryFormData(fieldList, head.getTableName(), id);
		if(head.getTableType()==2) {
			//如果他是主表 则还需要查询子表的数据 查询子表数据 需找到关联字段赋值
			String subTables = head.getSubTableStr();
			if(StringUtil.isNotBlank(subTables)){
				String[] arr = subTables.split(",");
				for (String tb : arr) {
					OnlCgformHead temp = this.baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, tb));
					if(temp==null) {
						continue;
					}
					List<OnlCgformField> subFieldList = this.fieldService.queryFormFields(temp.getId(),false);
					String sublinkField="",subLinkValue=null;
					for (OnlCgformField field : subFieldList) {
						if(oConvertUtils.isEmpty(field.getMainField())) {
							continue;
						}
						sublinkField = field.getDbFieldName();
						String mainField = field.getMainField();
						// 空指针 wcd?
						if(null == map.get(mainField)) {
							subLinkValue = map.get(mainField.toUpperCase()).toString();
						}else {
							subLinkValue = map.get(mainField).toString();
						}
					}

					List<Map<String, Object>> subList = this.fieldService.querySubFormData(subFieldList, tb, sublinkField, subLinkValue);
					if(subList==null || subList.size()==0) {
						map.put(tb, new String[] {});
					}else {
						map.put(tb, CgformUtil.toLowerCasePageList(subList));
					}
				}
			}

		}
		return map;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public String editManyFormData(String code, JSONObject json) throws DBException, BusinessException {
		OnlCgformHead head = this.getById(code);
		if (head == null) {
			throw new DBException("数据库主表ID["+code+"]不存在");
		}
		//执行java增强 事件类型start
		String currButtonCode = "edit";
		this.executeEnhanceJava(currButtonCode,"start", head, json);
				
		String tbname = head.getTableName();
		//如果是tree则 新增时 赋值：是否子节点
		if("Y".equals(head.getIsTree())) {
			this.fieldService.editTreeFormData(code, tbname, json,head.getTreeIdField(), head.getTreeParentIdField());
		}else {
			fieldService.editFormData(code, tbname, json,false);
		}
		//主子表逻辑
		if(head.getTableType()==2) {
			String subTables = head.getSubTableStr();
			if(StringUtil.isNotBlank(subTables)){
				String[] arr = subTables.split(",");
				for (String tb : arr) {
					OnlCgformHead temp = this.baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, tb));
					if(temp==null) {
						continue;
					}
					List<OnlCgformField> subFieldList = this.fieldService.list(new LambdaQueryWrapper<OnlCgformField>().eq(OnlCgformField::getCgformHeadId, temp.getId()));
					String sublinkField="",subLinkValue=null;
					for (OnlCgformField field : subFieldList) {
						if(oConvertUtils.isEmpty(field.getMainField())) {
							continue;
						}
						sublinkField = field.getDbFieldName();
						String mainField = field.getMainField();

						//update-begin-author:taoyan date:20190924 for:主子表关联字段取值bug
						if(json.get(mainField.toLowerCase())!=null){
							subLinkValue = json.getString(mainField.toLowerCase());
						}
						if(json.get(mainField.toUpperCase())!=null){
							subLinkValue = json.getString(mainField.toUpperCase());
						}
						//update-end-author:taoyan date:20190924 for:主子表关联字段取值bug
					}
					// 删除从表数据
					if(oConvertUtils.isEmpty(subLinkValue)) {
						continue;
					}
					fieldService.deleteAutoList(tb, sublinkField, subLinkValue);
					//新增数据
					JSONArray jsonArray = json.getJSONArray(tb);
					if(jsonArray==null || jsonArray.size()==0) {
						continue;
					}
					for(int i=0;i<jsonArray.size();i++) {
						JSONObject jsonObject = jsonArray.getJSONObject(i);
						if(subLinkValue!=null) {
							jsonObject.put(sublinkField, subLinkValue);//设置外键值
						}
						this.fieldService.saveFormData(subFieldList, tb, jsonObject);
					}
				}
			}

		}
		
		//执行java增强 事件类型end
		this.executeEnhanceJava(currButtonCode,"end", head, json);
		//执行SQL增强
		this.executeEnhanceSql(currButtonCode,code, json);
		return tbname;
	}

	/**
	 * 获取java增强 object对象
	 * @param buttonCode
	 * @param eventType
	 * @param headId
	 * @return
	 */
	private OnlCgformEnhanceJava getEnhanceByHeadId(String buttonCode, String eventType, String headId){
		LambdaQueryWrapper<OnlCgformEnhanceJava> query = new LambdaQueryWrapper<OnlCgformEnhanceJava>();
		query.eq(OnlCgformEnhanceJava::getActiveStatus, "1");
		query.eq(OnlCgformEnhanceJava::getButtonCode,buttonCode);
		query.eq(OnlCgformEnhanceJava::getEvent, eventType);//start和end之分
		query.eq(OnlCgformEnhanceJava::getCgformHeadId,headId);
		return this.onlCgformEnhanceJavaMapper.selectOne(query);
	}

	/**
	 * 获取java增强 object对象
	 * @param buttonCode
	 * @param eventType
	 * @param headId
	 * @return
	 */
	private Object getEnhanceJavaObject(String buttonCode, String eventType, String headId){
		LambdaQueryWrapper<OnlCgformEnhanceJava> query = new LambdaQueryWrapper<OnlCgformEnhanceJava>();
		query.eq(OnlCgformEnhanceJava::getActiveStatus, "1");
		query.eq(OnlCgformEnhanceJava::getButtonCode,buttonCode);
		query.eq(OnlCgformEnhanceJava::getEvent, eventType);//start和end之分
		query.eq(OnlCgformEnhanceJava::getCgformHeadId,headId);
		OnlCgformEnhanceJava enhance = this.onlCgformEnhanceJavaMapper.selectOne(query);
		Object obj = getEnhanceObject(enhance);
		return obj;
	}

	/**
	 * 执行java增强
	 * @param obj
	 * @param tableName
	 * @param json
	 * @return
	 * @throws BusinessException
	 */
	private int executeEnhanceJavaByJson(JSONObject json, Object obj, String tableName, OnlCgformEnhanceJava enhance) throws BusinessException {
		if(obj!=null && obj instanceof CgformEnhanceJavaInter){
			CgformEnhanceJavaInter javaInter = (CgformEnhanceJavaInter) obj;
			return javaInter.execute(tableName, json);
		} else if (obj!=null && obj instanceof CgformEnhanceHttpFormImpl) {
			// 兼容http增强
			return ((CgformEnhanceHttpFormImpl) obj).execute(tableName, json, enhance);
		}
		return CgformUtil.OL_IMPORT_INSERT;
	}


	@Override
	public int executeEnhanceJava(String buttonCode, String eventType,OnlCgformHead head,JSONObject json) throws BusinessException{
		OnlCgformEnhanceJava enhance = getEnhanceByHeadId(buttonCode, eventType, head.getId());
		Object obj = getEnhanceObject(enhance);
		int result = executeEnhanceJavaByJson(json, obj, head.getTableName(), enhance);
		return result;
	}

	@Override
	public void executeEnhanceExport(OnlCgformHead head, List<Map<String, Object>> dataList) throws BusinessException {
		this.executeEnhanceList(head,CgformUtil.ENHANCE_EXPORT,dataList);
	}

	@Override
	public void executeEnhanceList(OnlCgformHead head, String buttonCode, List<Map<String, Object>> dataList) throws BusinessException {
		LambdaQueryWrapper<OnlCgformEnhanceJava> query = new LambdaQueryWrapper<OnlCgformEnhanceJava>();
		query.eq(OnlCgformEnhanceJava::getActiveStatus, CgformUtil.ENHANCE_ACTIVE);
		query.eq(OnlCgformEnhanceJava::getButtonCode,buttonCode);
		query.eq(OnlCgformEnhanceJava::getCgformHeadId,head.getId());
		List<OnlCgformEnhanceJava> enhances = this.onlCgformEnhanceJavaMapper.selectList(query);
		if(enhances!=null && enhances.size()>0) {
			Object obj = getEnhanceObject(enhances.get(0));
			if(obj!=null && obj instanceof CgformEnhanceJavaListInter){
				CgformEnhanceJavaListInter javaInter = (CgformEnhanceJavaListInter) obj;
				javaInter.execute(head.getTableName(), dataList);
			} else if (obj!=null && obj instanceof CgformEnhanceHttpListImpl) {
				// 兼容httpList增强
				((CgformEnhanceHttpListImpl) obj).execute(head.getTableName(), dataList, enhances.get(0));
			}

		}
	}

	/**
	 * 根据OnlCgformEnhanceJava对象获取CgformEnhanceJavaInter对象
	 * @param enhance
	 * @return
	 */
	private Object getEnhanceObject(OnlCgformEnhanceJava enhance){
		if(enhance!=null){
			String javaType = enhance.getCgJavaType();
			String javaValue = enhance.getCgJavaValue();
			if(StringUtil.isNotBlank(javaValue)) {
				Object obj = null;
				if("class".equals(javaType)) {
					try {
						obj = MyClassLoader.getClassByScn(javaValue).newInstance();
					} catch (InstantiationException e) {
						log.error(e.getMessage(), e);
					} catch (IllegalAccessException e) {
						log.error(e.getMessage(), e);
					}
				}else if("spring".equals(javaType)){
					obj = SpringContextUtils.getBean(javaValue);
				} else if ("http".equals(javaType)) {
					obj = this.getHttpEnhanceObject(enhance);
				}
				return obj;
			}
		}
		return null;
	}

	/**
	 * 获取http增强类
	 *
	 * @param enhance
	 * @return
	 */
	private Object getHttpEnhanceObject(OnlCgformEnhanceJava enhance) {
		switch (enhance.getButtonCode()) {
			case CgformConstant.ENHANCE_JAVA_BUTTON_ADD:
			case CgformConstant.ENHANCE_JAVA_BUTTON_EDIT:
			case CgformConstant.ENHANCE_JAVA_BUTTON_DELETE:
			case CgformConstant.ENHANCE_JAVA_BUTTON_IMPORT:
				// 新增、编辑、删除、导入用 CgformEnhanceJavaHttpImpl 增强
				return this.cgformEnhanceJavaHttp;
			case CgformConstant.ENHANCE_JAVA_BUTTON_EXPORT:
			case CgformConstant.ENHANCE_JAVA_BUTTON_QUERY:
				// 导出、查询用 CgformEnhanceJavaListHttpImpl 增强
				return this.cgformEnhanceJavaListHttp;
		}
		// 默认走 CgformEnhanceJavaHttpImpl 增强
		return this.cgformEnhanceJavaHttp;
	}

	/**
	 * 查询sql增强对象
	 * @param buttonCode
	 * @param headId
	 * @return
	 */
	private OnlCgformEnhanceSql getEnhanceSqlObject(String buttonCode, String headId){
		LambdaQueryWrapper<OnlCgformEnhanceSql> query = new LambdaQueryWrapper<OnlCgformEnhanceSql>();
		query.eq(OnlCgformEnhanceSql::getButtonCode,buttonCode);
		query.eq(OnlCgformEnhanceSql::getCgformHeadId, headId);
		OnlCgformEnhanceSql enhance = this.onlCgformEnhanceSqlMapper.selectOne(query);
		return enhance;
	}

	/**
	 * 根据json数据 替换sql中的参数，获取完整的需要执行的sql并执行
	 * @param json
	 * @param enhance
	 */
	private void executeEnhanceSqlByJson(JSONObject json, OnlCgformEnhanceSql enhance){
		if(enhance!=null && StringUtil.isNotBlank(enhance.getCgbSql())) {
			String sqls = CgformUtil.formateSQl(enhance.getCgbSql(), json);
			String [] arr = sqls.split(";");
			for(String sql:arr){
				if(sql == null || sql.toLowerCase().trim().equals("")){
					continue;
				}
				//log.info(" online sql 增强： "+ sql);
				this.baseMapper.executeDDL(sql);
			}
		}
	}


	@Override
	public void executeEnhanceSql(String buttonCode, String formId, JSONObject json) {
		OnlCgformEnhanceSql enhance = getEnhanceSqlObject(buttonCode, formId);
		executeEnhanceSqlByJson(json, enhance);
	}



	@Override
	public void executeCustomerButton(String buttonCode, String formId, String dataId) throws BusinessException {
		OnlCgformHead head = this.getById(formId);
		if(head==null) {
			throw new BusinessException("未找到表配置信息");
		}
		OnlCgformEnhanceJava startEnhanceJava = getEnhanceByHeadId(buttonCode, "start", formId);
		OnlCgformEnhanceJava endEnhanceJava = getEnhanceByHeadId(buttonCode, "end", formId);
		Object startEnhanceJavaObj = getEnhanceObject(startEnhanceJava);
		Object endEnhanceJavaObj = getEnhanceObject(endEnhanceJava);
		OnlCgformEnhanceSql enhanceSql = getEnhanceSqlObject(buttonCode, formId);
		String tableName = head.getTableName();
		String[] arr = dataId.split(",");
		//update-begin-author:taoyan date:20220104 for:JTC-326【online】sql增强 达梦数据库数据库执行报错，因为blob类型问题导致
		//获取表字段配置信息
		LambdaQueryWrapper<OnlCgformField> query = new LambdaQueryWrapper<OnlCgformField>();
		query.eq(OnlCgformField::getCgformHeadId, formId);
		List<OnlCgformField> fieldList = this.onlCgformFieldService.list(query);
		for(String id: arr){
			Map<String,Object> data = this.baseMapper.queryOneByTableNameAndId(CgformUtil.getRealTableName(head.getTableName()), id);
			data = getSimpleData(fieldList, data);
			JSONObject json = JSONObject.parseObject(JSON.toJSONString(data));
			//执行java增强 事件类型start
			executeEnhanceJavaByJson(json, startEnhanceJavaObj, tableName, startEnhanceJava);
			//执行sql 增强
			executeEnhanceSqlByJson(json, enhanceSql);
			//执行java 增强 事件类型end
			executeEnhanceJavaByJson(json, endEnhanceJavaObj, tableName, endEnhanceJava);
		}
		//update-end-author:taoyan date:20220104 for:JTC-326【online】sql增强 达梦数据库数据库执行报错，因为blob类型问题导致
	}

	/**
	 * 排除掉blob、text类型的数据
	 * @param fieldList
	 * @param data
	 * @return
	 */
	private Map<String,Object> getSimpleData(List<OnlCgformField> fieldList, Map<String,Object> data){
		Map<String,Object> result = new HashMap<>();
		for(OnlCgformField field: fieldList){
			String type = field.getDbType();
			if("blob".equalsIgnoreCase(type) || "text".equalsIgnoreCase(type)){
				continue;
			}
			String key = field.getDbFieldName();
			Object value = CgformUtil.getMapValueObj(data, key);
			result.put(key, value);
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<OnlCgformButton> queryValidButtonList(String headId) {
		LambdaQueryWrapper<OnlCgformButton> query = new LambdaQueryWrapper<OnlCgformButton>();
		query.eq(OnlCgformButton::getCgformHeadId,headId);
		query.eq(OnlCgformButton::getButtonStatus, "1");
		query.orderByAsc(OnlCgformButton::getOrderNum);
		return  this.onlCgformButtonMapper.selectList(query);
		
	}


	@Override
	public OnlCgformEnhanceJs queryEnhanceJs(String formId, String cgJsType) {
		LambdaQueryWrapper<OnlCgformEnhanceJs> query = new LambdaQueryWrapper<OnlCgformEnhanceJs>();
		query.eq(OnlCgformEnhanceJs::getCgformHeadId,formId);
		query.eq(OnlCgformEnhanceJs::getCgJsType, cgJsType);
		return this.onlCgformEnhanceJsMapper.selectOne(query);
	}


	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteOneTableInfo(String formId, String dataId) throws BusinessException {
		OnlCgformHead head = this.getById(formId);
		if(head==null) {
			throw new BusinessException("未找到表配置信息");
		}
		//因为有复制视图的功能 所以需要加一个获取真实表名的方法
		String realTableName = CgformUtil.getRealTableName(head.getTableName());
		Map<String,Object> record = this.baseMapper.queryOneByTableNameAndId(realTableName, dataId);
		if(record==null) {
			// 【JTC-656】未找到数据忽略，不报错
			return;
			// throw new BusinessException("未找到数据信息");
		}
		//map的key转成小写 cloud大字段处理
		Map<String,Object> newRecord  = CgformUtil.transLowerCaseMapKey(record);
		String buttonCode = "delete";
		//执行java增强 事件类型start
		JSONObject json = JSONObject.parseObject(JSON.toJSONString(newRecord));
		this.executeEnhanceJava(buttonCode,"start", head, json);

		//如果是树节点 需要判断上级节点状态
		this.updateParentNode(head, dataId);

		if(head.getTableType()==2){
			this.fieldService.deleteAutoListMainAndSub(head,dataId);
		}else{
			String sql = "delete from "+realTableName+" where id = '"+dataId+"'";
			this.baseMapper.deleteOne(sql);
		}

		this.executeEnhanceSql(buttonCode, formId, json);
		//执行java 增强 事件类型end
		this.executeEnhanceJava(buttonCode,"end", head, json);
	}

	/**
	 * 该方法 专门给非online功能 调用online表单组件用的
	 * 已废弃 使用IOnlineService.queryFlowOnlineFormItem 替换
	 * @param head
	 * @return
	 */
	@Deprecated
	@Override
	public JSONObject queryFormItem(OnlCgformHead head, String username) {
		//查询表有效字段
		List<OnlCgformField> fieldList = this.fieldService.queryAvailableFields(head.getId(), head.getTableName(), head.getTaskId(),false);

		List<String> disabledFieldNameList = new ArrayList<String>();
		if(oConvertUtils.isEmpty(head.getTaskId())){
			//查询表online被禁用字段
//			List<String> disabledOnlineList = this.fieldService.queryDisabledFields(head.getTableName());
			List<String> disabledOnlineList = this.onlAuthPageService.queryFormDisabledCode(head.getId());
			if(disabledOnlineList!=null && disabledOnlineList.size()>0 && disabledOnlineList.get(0)!=null){
				disabledFieldNameList.addAll(disabledOnlineList);
			}
		}else{
			//查询表流程被禁用字段
			List<String> disabledFlowList = this.fieldService.queryDisabledFields(head.getTableName(),head.getTaskId());
			if(disabledFlowList!=null && disabledFlowList.size()>0 && disabledFlowList.get(0)!=null){
				disabledFieldNameList.addAll(disabledFlowList);
			}
		}

		//组装schema
		JSONObject schema = CgformUtil.getJsonSchemaByCgformFieldList(fieldList,disabledFieldNameList,null);
		if(head.getTableType()==2) {
			//主表
			String subStr = head.getSubTableStr();
			if(StringUtil.isNotBlank(subStr)) {
				for (String tbname : subStr.split(",")) {
					OnlCgformHead tempTable = this.baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, tbname));
					if(tempTable==null) {
						continue;
					}

					//update-begin-author:lvdandan date:20201113 for: LOWCOD-704 【流程】节点权限配置online字段权限做成下拉框加载
					//查询子表字段
					List<OnlCgformField> subFieldList = this.fieldService.queryAvailableFields(tempTable.getId(), tempTable.getTableName(), head.getTaskId(),false);

					//查询被禁用字段
					List<String> subDisabledFieldNameList = new ArrayList<String>();
					if(StringUtil.isNotBlank(head.getTaskId())){
						//查询流程中被禁用字段
						subDisabledFieldNameList = this.fieldService.queryDisabledFields(tempTable.getTableName(),head.getTaskId());
					}else{
						//update-begin-author:taoyan date:20200723 for:online权限改造
						//查询菜单中被禁用字段
//						subDisabledFieldNameList = this.fieldService.queryDisabledFields(tempTable.getTableName());
						subDisabledFieldNameList = this.onlAuthPageService.queryFormDisabledCode(tempTable.getId());
						//update-end-author:taoyan date:20200723 for:online权限改造
					}
					//update-end-author:lvdandan date:20201113 for: LOWCOD-704 【流程】节点权限配置online字段权限做成下拉框加载

					JSONObject subJson = new JSONObject();
					if(1==tempTable.getRelationType()) {
						//one to one 
						subJson = CgformUtil.getJsonSchemaByCgformFieldList(subFieldList,subDisabledFieldNameList,null);
					}else {
						//one to many
						subJson.put("columns",  CgformUtil.getJEditableTableColumnsJson(subFieldList,subDisabledFieldNameList));
					}
					subJson.put("relationType", tempTable.getRelationType());// 0-1对多 1-1对1
					subJson.put("view", "tab");
					subJson.put("order",tempTable.getTabOrderNum());
					subJson.put("formTemplate", tempTable.getFormTemplate());
					subJson.put("describe", tempTable.getTableTxt());
					subJson.put("key", tempTable.getTableName());
					schema.getJSONObject("properties").put(tempTable.getTableName(), subJson);
				}
			}
		}
		//log.info("----动态表单获取JSON-SCHEMA>>"+schema.toJSONString());
		return schema;
	}

	@Override
	public List<String> generateCode(OnlGenerateModel model) throws Exception {
		TableVo tableVo = new TableVo();
		tableVo.setEntityName(model.getEntityName());
		tableVo.setEntityPackage(model.getEntityPackage());
		tableVo.setFtlDescription(model.getFtlDescription());
		tableVo.setTableName(model.getTableName());
		tableVo.setSearchFieldNum(-1);
		List<ColumnVo> columns = new ArrayList<ColumnVo>();
		List<ColumnVo> originalColumns = new ArrayList<ColumnVo>();
		initColumnVoList(model.getCode(),columns, originalColumns);
		//新增树形列表的所需参数
		OnlCgformHead tableConfig = this.baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getId, model.getCode()));
		Map<String,String> extendParams = new HashMap<String,String>();
		extendParams.put("scroll",tableConfig.getScroll()==null?"0":tableConfig.getScroll().toString());
		String formTemplate = tableConfig.getFormTemplate();
		if(oConvertUtils.isEmpty(formTemplate)){
			tableVo.setFieldRowNum(1);
		}else{
			tableVo.setFieldRowNum(Integer.parseInt(formTemplate));
		}
		if("Y".equals(tableConfig.getIsTree())) {
			extendParams.put("pidField", tableConfig.getTreeParentIdField());
			extendParams.put("hasChildren", tableConfig.getTreeIdField());
			extendParams.put("textField", tableConfig.getTreeFieldname());
		}
		tableVo.setExtendParams(extendParams);

		CgformEnum templateCgformEnum = CgformEnum.getCgformEnumByConfig(model.getJspMode());
		List<String> generateList = new CodeGenerateOne(tableVo, columns,originalColumns).generateCodeFile(model.getProjectPath(),templateCgformEnum.getTemplatePath(),templateCgformEnum.getStylePath());
		//update-begin-author:scott date:20210202 --- for:建议优化Online表单开发代码生成器不能成功生成代码的错误日志 #2234
		if(generateList==null || generateList.size()==0){
			generateList = new ArrayList<>();
			generateList.add(" :::::: 生成失败ERROR提醒 :::::: ");
			generateList.add("1.未找到代码生成器模板，请确认路径是否含有中文或特殊字符！");
			generateList.add("2.如果是JAR包运行，请参考此文档 http://doc.jeecg.com/2043922");
		}
		//update-end-author:scott date:20210202 --- for:建议优化Online表单开发代码生成器不能成功生成代码的错误日志 #2234
		return generateList;
	}


	@Override
	public List<String> generateOneToMany(OnlGenerateModel model) throws Exception {
		MainTableVo mainTableVo = new MainTableVo();
		mainTableVo.setEntityName(model.getEntityName());
		mainTableVo.setEntityPackage(model.getEntityPackage());
		mainTableVo.setFtlDescription(model.getFtlDescription());
		mainTableVo.setTableName(model.getTableName());
		OnlCgformHead tableConfig = this.baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getId, model.getCode()));
		String formTemplate = tableConfig.getFormTemplate();
		if(oConvertUtils.isEmpty(formTemplate)){
			mainTableVo.setFieldRowNum(1);
		}else{
			mainTableVo.setFieldRowNum(Integer.parseInt(formTemplate));
		}
		List<ColumnVo> mainColums = new ArrayList<ColumnVo>();
		List<ColumnVo> originalMainColumns = new ArrayList<ColumnVo>();
		initColumnVoList(model.getCode(),mainColums, originalMainColumns);
		
		List<OnlGenerateModel> subList = model.getSubList();
		List<SubTableVo> subTables = new ArrayList<SubTableVo>();
		for (OnlGenerateModel sub : subList) {
			OnlCgformHead subTableConfig = this.baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, sub.getTableName()));
			if(subTableConfig==null) {
				continue;
			}
			SubTableVo subVo = new SubTableVo();
			subVo.setEntityName(sub.getEntityName());
			subVo.setEntityPackage(model.getEntityPackage());//子表和主表代码放在一个包里
			subVo.setTableName(sub.getTableName());
			subVo.setFtlDescription(sub.getFtlDescription());
			Integer manyOrone = subTableConfig.getRelationType();//映射关系  1一对一  0一对多
			subVo.setForeignRelationType(manyOrone==1?"1":"0");
			
			List<ColumnVo> columns = new ArrayList<ColumnVo>();
			List<ColumnVo> originalColumns = new ArrayList<ColumnVo>();
			OnlCgformField foreignKeys = initColumnVoList(subTableConfig.getId(),columns, originalColumns);
			if(foreignKeys==null) {
				continue;
			}
			subVo.setOriginalForeignKeys(new String[] {foreignKeys.getDbFieldName()});
			subVo.setForeignKeys(new String[] {foreignKeys.getDbFieldName()});
			subVo.setColums(columns);
			subVo.setOriginalColumns(originalColumns);
			subTables.add(subVo);
		}
		CgformEnum templateCgformEnum = CgformEnum.getCgformEnumByConfig(model.getJspMode());
		List<String> generateList = new CodeGenerateOneToMany(mainTableVo, mainColums, originalMainColumns, subTables).generateCodeFile(model.getProjectPath(),templateCgformEnum.getTemplatePath(),templateCgformEnum.getStylePath());
		return generateList;
	}
	
	/**
	  * 装载ColumnVo 集合
	 * @param columns
	 * @param originalColumns
	 * @param cgConfigId 配置表ID
	 * @return 返回外键的那个字段配置信息 主表可忽略
	 */
	@SuppressWarnings("unchecked")
	private OnlCgformField initColumnVoList(String cgConfigId,List<ColumnVo> columns,List<ColumnVo> originalColumns) {
		LambdaQueryWrapper<OnlCgformField> query = new LambdaQueryWrapper<OnlCgformField>();
		query.eq(OnlCgformField::getCgformHeadId,cgConfigId);
		query.orderByAsc(OnlCgformField::getOrderNum);
		List<OnlCgformField> fieldList = this.fieldService.list(query);
		OnlCgformField foreignKeys = null;
		for (OnlCgformField f : fieldList) {
			if(StringUtil.isNotBlank(f.getMainTable())) {
				foreignKeys = f;
			}
			ColumnVo vo = new ColumnVo();
			vo.setFieldLength(f.getFieldLength());
			vo.setFieldHref(f.getFieldHref());
			vo.setFieldValidType(f.getFieldValidType());
			vo.setFieldDefault(f.getDbDefaultVal());
			vo.setFieldShowType(f.getFieldShowType());
			vo.setFieldOrderNum(f.getOrderNum());
			vo.setIsKey(f.getDbIsKey()==1?"Y":"N");
			vo.setIsShow(f.getIsShowForm()==1?"Y":"N");
			vo.setIsShowList(f.getIsShowList()==1?"Y":"N");
			vo.setIsQuery(f.getIsQuery()==1?"Y":"N");
			vo.setQueryMode(f.getQueryMode());
			vo.setDictField(f.getDictField());
			// update-begin-author:taoyan date:20220112 for: /issues/3312 生成代码后，使用字典table并且添加where条件的字典下拉框异常
			vo.setDictTable(f.getDictTable());
			// update-end-author:taoyan date:20220112 for: /issues/3312 生成代码后，使用字典table并且添加where条件的字典下拉框异常
			vo.setDictText(f.getDictText());
			vo.setFieldDbName(f.getDbFieldName());
			vo.setFieldName(oConvertUtils.camelName(f.getDbFieldName()));
			vo.setFiledComment(f.getDbFieldTxt());
			vo.setFieldDbType(f.getDbType());
			vo.setFieldType(getFiledType(f.getDbType()));
			vo.setClassType(f.getFieldShowType());
			vo.setClassType_row(f.getFieldShowType());

			//update-begin-author:taoyan date:20190902 代码生成器 必填校验
			if(f.getDbIsNull()==0 || "*".equals(f.getFieldValidType()) || "1".equals(f.getFieldMustInput())) {
				vo.setNullable("N");
			}else {
				vo.setNullable("Y");
			}
			//update-end-author:taoyan date:20190902 代码生成器 必填校验
			//如果是开关组件 则需要将开关组件的参数传到代码生成器里面
			if(CgformUtil.SWITCH.equals(f.getFieldShowType())){
				if(StringUtil.isNotBlank(f.getFieldExtendJson())){
					vo.setDictField(f.getFieldExtendJson());
				}else{
					//设置默认字典code
					vo.setDictField("is_open");
				}
			}
			// update-begin-author:sunjianlei date:20210414 通用扩展字段，将FieldExtendJson转为ExtendParamsMap
			Map<String, Object> extendParams = new HashMap<>();
			if (org.apache.commons.lang.StringUtil.isNotBlank(f.getFieldExtendJson())) {
				try {
					JSONObject json = JSONObject.parseObject(f.getFieldExtendJson());
					if (json != null) {
						// 合并到一起
						extendParams.putAll(json.getInnerMap());
					}
				} catch (JSONException ignored) {
				}
			}
			vo.setExtendParams(extendParams);
			// update-end-author:sunjianlei date:20210414 通用扩展字段，将FieldExtendJson转为ExtendParamsMap

			// update-begin-author:sunjianlei date:20210412 Popup扩展字段，支持配置是否多选
			if (CgformUtil.POPUP.equals(f.getFieldShowType())) {
				// Popup组件扩展参数：是否多选，默认为true
				boolean popupMulti = true;
				Object popupMultiObj = extendParams.get(ExtendJsonKey.POPUP_MULTI);
				if (popupMultiObj != null) {
					popupMulti = (boolean) popupMultiObj;
				}
				extendParams.put(ExtendJsonKey.POPUP_MULTI, popupMulti);
			}
			// update-end-author:sunjianlei date:20210412 Popup扩展字段，支持配置是否多选

			vo.setSort("1".equals(f.getSortFlag())?"Y":"N");
			//只读禁用
			vo.setReadonly(Integer.valueOf(1).equals(f.getIsReadOnly())?"Y":"N");
			//JT-199 控件默认值(暂时不支持{{...}}、#{...}、${...})
			if(StringUtil.isNotBlank(f.getFieldDefaultValue()) && !f.getFieldDefaultValue().trim().startsWith("${") && !f.getFieldDefaultValue().trim().startsWith("#{") && !f.getFieldDefaultValue().trim().startsWith("{{")){
				vo.setDefaultVal(f.getFieldDefaultValue());
			}
			//如果是上传组件，则传设置参数 上传文件数量
			if (CgformUtil.FILE.equals(f.getFieldShowType()) || CgformUtil.IMAGE.equals(f.getFieldShowType())) {
				if (StringUtil.isNotBlank(f.getFieldExtendJson())) {
					JSONObject json = JSONObject.parseObject(f.getFieldExtendJson());
					if(StringUtil.isNotBlank(json.getString(ExtendJsonKey.UPLOAD_NUM))){
						vo.setUploadnum(json.getString(ExtendJsonKey.UPLOAD_NUM));
					}
				}
			}

			originalColumns.add(vo);
			//update-begin-author:sunjlei--date:20210412--for: issues/I3EL13
			if(f.getIsShowForm()==1||f.getIsShowList()==1||f.getIsQuery()==1) {
				columns.add(vo);
			}
			//update-end-author:sunjlei--date:20210412--for: issues/I3EL13
		}
		return foreignKeys;
	}
	
	
	/**
	 * 根据数据库类型获取java类型
	 * @param dbType
	 * @return
	 */
	private String getFiledType(String dbType) {
		dbType = dbType.toLowerCase();
		if(dbType.indexOf("int")>=0) {
			return "java.lang.Integer";
		}else if(dbType.indexOf("double")>=0) {
			return "java.lang.Double";
		}else if(dbType.indexOf("decimal")>=0) {
			return "java.math.BigDecimal";
		}else if(dbType.indexOf("date")>=0) {
			return "java.util.Date";
		}else {
			return "java.lang.String";
		}
	}



	@Override
	public void addCrazyFormData(String tbname, JSONObject json) throws DBException, UnsupportedEncodingException {
		OnlCgformHead head = this.getOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, tbname));
		if (head == null) {
			throw new DBException("数据库主表["+tbname+"]不存在");
		}
		if(head.getTableType()==2) {
			//如果他是主表 则还需要保存子表的数据
			String subTables = head.getSubTableStr();
			if (subTables != null) {
				String[] arr = subTables.split(",");
				for (String tb : arr) {
					String subText = json.getString(CgformUtil.SUB_TABLE_PRE + tb);
					if (oConvertUtils.isEmpty(subText)) {
						// update-begin--author:sunjianlei  date:20210618 for： 表单设计器同步Online表单新增支持一对一 -------------------
						subText = json.getString(CgformUtil.SUB_TABLE_ONE2ONE_PRE + tb);
						if (oConvertUtils.isEmpty(subText)) {
							continue;
						}
						// update-end--author:sunjianlei  date:20210618 for： 表单设计器同步Online表单新增支持一对一 -------------------
					}
					JSONArray jsonArray = JSONArray.parseArray(URLDecoder.decode(subText, "UTF-8"));
					if(jsonArray==null || jsonArray.size()==0) {
						continue;
					}

					OnlCgformHead temp = this.baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, tb));
					if(temp==null) {
						continue;
					}

					List<OnlCgformField> subFieldList = this.fieldService.list(new LambdaQueryWrapper<OnlCgformField>().eq(OnlCgformField::getCgformHeadId, temp.getId()));
					//这里有一步非常重要 需要获取子表字段对应设置外键的那个字段然后赋值
					String sublinkField="",subLinkValue=null;
					for (OnlCgformField field : subFieldList) {
						if(oConvertUtils.isEmpty(field.getMainField())) {
							continue;
						}
						sublinkField = field.getDbFieldName();
						String mainField = field.getMainField();
						subLinkValue = json.getString(mainField);
					}

					for(int i=0;i<jsonArray.size();i++) {
						JSONObject jsonObject = jsonArray.getJSONObject(i);
						if(subLinkValue!=null) {
							jsonObject.put(sublinkField, subLinkValue);//设置外键值
						}
						fieldService.executeInsertSQL(CgformUtil.getCrazyFormDataSaveSql(tb, subFieldList, jsonObject));
					}
				}
			}
		}
		this.fieldService.saveFormData(head.getId(), tbname, json, true);
	}



	@Override
	public void editCrazyFormData(String tbname, JSONObject json) throws DBException, UnsupportedEncodingException  {
		OnlCgformHead head = this.getOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, tbname));
		if (head == null) {
			throw new DBException("数据库主表["+tbname+"]不存在");
		}
		if(head.getTableType()==2) {
			String subTables = head.getSubTableStr();
            //issues/I49F81 Online在线表单保存失败问题
			if(StringUtil.isNotBlank(subTables)){
				String[] arr = subTables.split(",");
				for (String tb : arr) {
					OnlCgformHead temp = this.baseMapper.selectOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, tb));
					if(temp==null) {
						continue;
					}
					List<OnlCgformField> subFieldList = this.fieldService.list(new LambdaQueryWrapper<OnlCgformField>().eq(OnlCgformField::getCgformHeadId, temp.getId()));
					String sublinkField="",subLinkValue=null;
					for (OnlCgformField field : subFieldList) {
						if(oConvertUtils.isEmpty(field.getMainField())) {
							continue;
						}
						sublinkField = field.getDbFieldName();
						String mainField = field.getMainField();
						subLinkValue = json.getString(mainField);
					}
					// 删除从表数据
					if(oConvertUtils.isEmpty(subLinkValue)) {
						continue;
					}
					fieldService.deleteAutoList(tb, sublinkField, subLinkValue);
					//新增数据
					String subText = json.getString(CgformUtil.SUB_TABLE_PRE + tb);
					// update-begin--author:sunjianlei  date:20210722 for： 表单设计器同步Online表单修改支持一对一 -------------------
					if (oConvertUtils.isEmpty(subText)) {
						subText = json.getString(CgformUtil.SUB_TABLE_ONE2ONE_PRE + tb);
						if (oConvertUtils.isEmpty(subText)) {
							continue;
						}
					}
					// update-end--author:sunjianlei  date:20210722 for： 表单设计器同步Online表单修改支持一对一 -------------------
					if(oConvertUtils.isEmpty(subText)) {
						continue;
					}
					JSONArray jsonArray = JSONArray.parseArray(URLDecoder.decode(subText, "UTF-8"));
					if(jsonArray==null || jsonArray.size()==0) {
						continue;
					}
					for(int i=0;i<jsonArray.size();i++) {
						JSONObject jsonObject = jsonArray.getJSONObject(i);
						if(subLinkValue!=null) {
							jsonObject.put(sublinkField, subLinkValue);//设置外键值
						}
						fieldService.executeInsertSQL(CgformUtil.getCrazyFormDataSaveSql(tb, subFieldList, jsonObject));
					}
				}
			}
		}
		this.fieldService.editFormData(head.getId(), tbname, json, true);
		
	}



	@Override
	public Integer getMaxCopyVersion(String physicId) {
		Integer v = baseMapper.getMaxCopyVersion(physicId);
		return v==null?0:v;
	}


	@Override
	public void copyOnlineTableConfig(OnlCgformHead physicTable) throws Exception {
		String id = physicTable.getId();
		OnlCgformHead copy = new OnlCgformHead();
		String copyid = UUIDGenerator.generate();
		copy.setId(copyid);
		//复制表的属性值3
		copy.setPhysicId(id);
		copy.setCopyType(1);
		copy.setCopyVersion(physicTable.getTableVersion());

		//原始表基本信息 12
		copy.setTableVersion(1);
		copy.setTableName(getUsefulTableName(id, physicTable.getTableName()));
		copy.setTableTxt(physicTable.getTableTxt());
		copy.setFormCategory(physicTable.getFormCategory());
		copy.setFormTemplate(physicTable.getFormTemplate());
		copy.setFormTemplateMobile(physicTable.getFormTemplateMobile());
		copy.setIdSequence(physicTable.getIdSequence());
		copy.setIdType(physicTable.getIdType());
		copy.setIsCheckbox(physicTable.getIsCheckbox());
		copy.setIsPage(physicTable.getIsPage());
		copy.setIsTree(physicTable.getIsTree());
		copy.setQueryMode(physicTable.getQueryMode());

		//复制表全设置为单表2
		copy.setTableType(1);
		copy.setIsDbSynch("N");

		//复制表设置的，表单设计器表单配置
		copy.setIsDesForm(physicTable.getIsDesForm());
		copy.setDesFormCode(physicTable.getDesFormCode());

		//树表属性3
		copy.setTreeParentIdField(physicTable.getTreeParentIdField());
		copy.setTreeFieldname(physicTable.getTreeFieldname());
		copy.setTreeIdField(physicTable.getTreeIdField());
		
		//复制表全设置为单表 主子表需要设置的属性全设置为null 3
		copy.setRelationType(null);
		copy.setTabOrderNum(null);
		copy.setSubTableStr(null);

		//update-begin-author:taoyan date:20200411 for:新增字段 复制视图用到
		copy.setThemeTemplate(physicTable.getThemeTemplate());
		copy.setScroll(physicTable.getScroll());
		//update-end-author:taoyan date:20200411 for:新增字段 复制视图用到
		copy.setExtConfigJson(physicTable.getExtConfigJson());

		List<OnlCgformField> fieldList = this.fieldService.list(new LambdaQueryWrapper<OnlCgformField>().eq(OnlCgformField::getCgformHeadId, id));
		for (OnlCgformField f : fieldList) {
			OnlCgformField field = new OnlCgformField();
			field.setCgformHeadId(copyid);
			copyOnlCgformField(f, field);
			
			this.fieldService.save(field);
		}
		baseMapper.insert(copy);
	}

	


	@Override
	public void initCopyState(List<OnlCgformHead> headList) {
		List<String> idList = baseMapper.queryCopyPhysicId();
		for (OnlCgformHead temp : headList) {
			if(idList.contains(temp.getId())) {
				temp.setHascopy(1);
			}else {
				temp.setHascopy(0);
			}
		}
	}

	@Override
	public void deleteBatch(String ids, String flag) {
		String[] arr = ids.split(",");
		if(CgformUtil.REAL_DELETE.equals(flag)){
			for(String id:arr){
				try {
					this.deleteRecordAndTable(id);
				} catch (DBException e) {
					e.printStackTrace();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}else{
			this.removeByIds(Arrays.asList(arr));
		}
	}

	@Override
	public void updateParentNode(OnlCgformHead head, String dataId) {
		if(CgformUtil.STATUS_YES.equals(head.getIsTree())){
			String tbname = CgformUtil.getRealTableName(head.getTableName());
			String pidField = head.getTreeParentIdField();
			Map<String,Object> param = baseMapper.queryOneByTableNameAndId(tbname,dataId);
			String pid = null;
			if(param.get(pidField)!=null && !"0".equals(param.get(pidField))){
				pid = param.get(pidField).toString();
			}else if(param.get(pidField.toUpperCase())!=null && !"0".equals(param.get(pidField.toUpperCase()))){
				pid = param.get(pidField.toUpperCase()).toString();
			}
			if(pid!=null){
				Integer cnt = baseMapper.queryChildNode(tbname,pidField,pid);
				if(cnt==1){
					String hasChild = head.getTreeIdField();
					fieldService.updateTreeNodeNoChild(tbname,hasChild,pid);
				}
			}
		}
	}

	/**
	 * 同步复制表的列信息
	 * @param head
	 * @param fields
	 */
	private void handleCopyTableColumns(OnlCgformHead head,List<OnlCgformField> fields) {
		List<OnlCgformHead> copyList = this.list(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getPhysicId, head.getId()));
		
		if(copyList!=null && copyList.size()>0) {
			for (OnlCgformHead copy : copyList) {
				
				List<OnlCgformField> old_columns = fieldService.list(new LambdaQueryWrapper<OnlCgformField>().eq(OnlCgformField::getCgformHeadId, copy.getId()));
				if(old_columns==null || old_columns.size()==0) {
					//如果复制表不存在列 那么直接拿到前端传过来的列保存一下即可
					for (OnlCgformField temp : fields) {
						OnlCgformField col = new OnlCgformField();
						col.setCgformHeadId(copy.getId());
						copyOnlCgformField(temp, col);
						this.fieldService.save(col);
					}
				}else {
					//如果复制表存在列,比较不相同
					Map<String,Integer> oldMap = new HashMap<String,Integer>();
					for (OnlCgformField old : old_columns) {
						oldMap.put(old.getDbFieldName(), 1);
					}
					
					Map<String,Integer> newMap = new HashMap<String,Integer>();
					for (OnlCgformField temp : fields) {
						newMap.put(temp.getDbFieldName(), 1);
					}

					List<String> common_columns = new ArrayList<String>();
					
					List<String> new_columns = new ArrayList<String>();
					for (String fieldName : newMap.keySet()) {
						if(oldMap.get(fieldName)==null) {
							//新的集合里有，而老的集合里不存在这一列，说明这一列 是新增的
							new_columns.add(fieldName);
						}else{
							// 共有的字段 需要比较字段的数据库配置
							common_columns.add(fieldName);
						}
					}
					
					List<String> del_columns = new ArrayList<String>();
					for (String fieldName : oldMap.keySet()) {
						if(newMap.get(fieldName)==null) {
							//老的集合里有，新的集合里没有，说明这一列需要被删除
							del_columns.add(fieldName);
						}
					}
					
					
					//处理删除
					if(del_columns.size()>0) {
						for (OnlCgformField temp : old_columns) {
							if(del_columns.contains(temp.getDbFieldName())) {
								this.fieldService.removeById(temp.getId());
							}
						}
					}
					
					//处理新增
					if(new_columns.size()>0) {
						for (OnlCgformField temp : fields) {
							if(new_columns.contains(temp.getDbFieldName())) {
								OnlCgformField col = new OnlCgformField();
								col.setCgformHeadId(copy.getId());
								copyOnlCgformField(temp, col);
								this.fieldService.save(col);
							}
						}
					}

					//当原表字段 数据库配置被修改 复制表跟着改
					if(common_columns.size()>0){
						for (String fieldName : common_columns) {
							updateCopyColumn(fieldName,fields,old_columns);
						}
					}
					
				}
				
			}
			
		}
	}

	/**
	 * online 复制表 当原表字段 数据库配置被修改 复制表跟着改
	 * @param fieldName
	 * @param newCols
	 * @param copyCols
	 */
	private void updateCopyColumn(String fieldName,List<OnlCgformField> newCols,List<OnlCgformField> copyCols){

		OnlCgformField newCol = null;
		for (OnlCgformField temp : newCols) {
			if(fieldName.equals(temp.getDbFieldName())){
				newCol = temp;
			}
		}

		OnlCgformField copyCol = null;
		for (OnlCgformField temp : copyCols) {
			if(fieldName.equals(temp.getDbFieldName())){
				copyCol = temp;
			}
		}

		if(newCol!=null && copyCol!=null){
			boolean flag = false;
			if(!newCol.getDbType().equals(copyCol.getDbType())){
				copyCol.setDbType(newCol.getDbType());
				flag = true;
			}
			if(!newCol.getDbDefaultVal().equals(copyCol.getDbDefaultVal())){
				copyCol.setDbDefaultVal(newCol.getDbDefaultVal());
				flag = true;
			}
			if(newCol.getDbLength()!=copyCol.getDbLength()){
				copyCol.setDbLength(newCol.getDbLength());
				flag = true;
			}
			if(newCol.getDbIsNull()!=copyCol.getDbIsNull()){
				copyCol.setDbIsNull(newCol.getDbIsNull());
				flag = true;
			}
			if(flag){
				this.fieldService.updateById(copyCol);
			}
		}

	}
	
	/**
	 * 属性复制
	 * @param meta
	 * @param dest
	 */
	private void copyOnlCgformField(OnlCgformField meta,OnlCgformField dest) {
		dest.setDbDefaultVal(meta.getDbDefaultVal());
		dest.setDbFieldName(meta.getDbFieldName());
		dest.setDbFieldNameOld(meta.getDbFieldNameOld());
		dest.setDbFieldTxt(meta.getDbFieldTxt());
		dest.setDbIsKey(meta.getDbIsKey());
		dest.setDbIsNull(meta.getDbIsNull());
		dest.setDbLength(meta.getDbLength());
		dest.setDbPointLength(meta.getDbPointLength());
		dest.setDbType(meta.getDbType());
		dest.setDictField(meta.getDictField());
		dest.setDictTable(meta.getDictTable());
		dest.setDictText(meta.getDictText());
		dest.setFieldExtendJson(meta.getFieldExtendJson());
		dest.setFieldHref(meta.getFieldHref());
		dest.setFieldLength(meta.getFieldLength());
		
		dest.setFieldMustInput(meta.getFieldMustInput());
		dest.setFieldShowType(meta.getFieldShowType());
		dest.setFieldValidType(meta.getFieldValidType());
		dest.setFieldDefaultValue(meta.getFieldDefaultValue());
		dest.setIsQuery(meta.getIsQuery());
		dest.setIsShowForm(meta.getIsShowForm());
		dest.setIsShowList(meta.getIsShowList());
		dest.setMainField(null);
		dest.setMainTable(null);
		dest.setOrderNum(meta.getOrderNum());
		dest.setQueryMode(meta.getQueryMode());

		//update-begin-author:taoyan date:20200411 for:新增字段 复制视图用到
		dest.setIsReadOnly(meta.getIsReadOnly());
		dest.setSortFlag(meta.getSortFlag());
		dest.setQueryDefVal(meta.getQueryDefVal());
		dest.setQueryConfigFlag(meta.getQueryConfigFlag());
		dest.setQueryDictField(meta.getQueryDictField());
		dest.setQueryDictTable(meta.getQueryDictTable());
		dest.setQueryDictText(meta.getQueryDictText());
		dest.setQueryMustInput(meta.getQueryMustInput());
		dest.setQueryShowType(meta.getQueryShowType());
		dest.setQueryValidType(meta.getQueryValidType());
		dest.setConverter(meta.getConverter());
		//update-end-author:taoyan date:20200411 for:新增字段 复制视图用到
	}

	/**
	 * 保存编辑online表单字段的时候需要作些特殊处理
	 * @param field
	 */
	private void specialFieldHandler(OnlCgformField field){
		if("Text".equals(field.getDbType())||"Blob".equals(field.getDbType())){
			field.setDbLength(0);
			field.setDbPointLength(0);
		}
	}

	/**
	 * 获取可用的表名 将所有复制表的表名获取到然后 得到最大的序号 +1
	 * @return
	 */
	private String getUsefulTableName(String physicId,String metaName){
		List<String> ls = baseMapper.queryAllCopyTableName(physicId);
		int max = 0;
		if(ls!=null || ls.size()>0){
			for(int i=0;i<ls.size();i++){
				String name = ls.get(i);
				int k = Integer.parseInt(name.split("\\"+CgformUtil.ONLINE_TABLE_SPLIT_STR)[1]);
				if(k>max){
					max = k;
				}
			}
		}
		return metaName+CgformUtil.ONLINE_TABLE_SPLIT_STR+(++max);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public String deleteDataByCode(String cgformCode, String dataIds) {
		//批量删除
		OnlCgformHead head = super.getOne(new LambdaQueryWrapper<OnlCgformHead>().eq(OnlCgformHead::getTableName, cgformCode));
		if (head == null) {
			throw new JeecgBootException("实体不存在");
		}
		String tableName = head.getTableName();
		try {
			if (dataIds.indexOf(",") > 0) {
				//批量删除
				onlCgformFieldService.deleteAutoListById(tableName, dataIds);
			} else {
				//删除一条记录 走增强逻辑 批量删除没走增强逻辑
				this.deleteOneTableInfo(head.getId(), dataIds);
			}
		} catch (Exception e) {
			log.error("OnlCgformApiController.formEdit()发生异常：" + e.getMessage(), e);
			throw new JeecgBootException("删除失败：" + e.getMessage());
		}
		return tableName;
	}


	/**
	 * online表单发起流程
	 * @param json
	 * @param tableName
	 * @param headId
	 * @throws Exception
	 */
	private void submitOnlineFlow(JSONObject json, String tableName,String headId) throws Exception {
		if(json.containsKey(CgformUtil.FLOW_SUBMIT_FLAG) && "1".equals(json.getString(CgformUtil.FLOW_SUBMIT_FLAG))){
			String username = JwtUtil.getUserNameByToken(SpringContextUtils.getHttpServletRequest());
			String flowCode = CgformUtil.FLOW_CODE_PRE+tableName;
			String formId = oConvertUtils.getString(json.getString("id"),json.getString("ID"));
			bpmBaseExtAPI.startMutilProcess(flowCode, formId, CgformUtil.FLOW_FORM_URL, CgformUtil.FLOW_MOBILE_FORM_URL, username, null);
		}else{
			//update-begin--- author:scott---date:20200313------for:移动OA统一草稿箱功能（online表单+设计器表单）,在保存表单时，判断关联流程的话则往流程中间表插入表单数据------------
			// 执行流程草稿保存
			List<OnlCgformField> subFieldList = this.fieldService.list(new LambdaQueryWrapper<OnlCgformField>().eq(OnlCgformField::getCgformHeadId, headId).eq(OnlCgformField::getDbFieldName,DataBaseConstant.BPM_STATUS_TABLE));
			if ((subFieldList!=null && subFieldList.size()>0) || oConvertUtils.isNotEmpty(json.get(DataBaseConstant.BPM_STATUS_TABLE)) || oConvertUtils.isNotEmpty(json.get(DataBaseConstant.BPM_STATUS_TABLE.toUpperCase()))) {
				try {
					String username = JwtUtil.getUserNameByToken(SpringContextUtils.getHttpServletRequest());
					Result<String> res = bpmBaseExtAPI.saveMutilProcessDraft(CgformUtil.FLOW_CODE_PRE + tableName, json.get("id").toString(),CgformUtil.FLOW_FORM_URL,CgformUtil.FLOW_MOBILE_FORM_URL,username, null);
					//log.info("保存流程草稿 dataId : " + res.getResult());

//				// 请求 header
//				HttpHeaders headers = new HttpHeaders();
//				headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
//				headers.set("Accept", MediaType.APPLICATION_JSON_UTF8_VALUE);
//				headers.set("X-Access-Token", xAccessToken);
//				//请求参数
//				JSONObject entity = new JSONObject();
//				entity.put("flowCode","onl_"+head.getTableName());
//				entity.put("id",json.get("id"));
//				entity.put("formUrl","modules/bpm/task/form/OnlineFormDetail");
//				entity.put("formUrlMobile","online/OnlineDetailForm");
//				//String url = RestUtil.getBaseUrl() + "/act/process/extActProcess/saveMutilProcessDraft";
//				JSONObject res = RestUtil.request(url, HttpMethod.POST, headers, null, entity, JSONObject.class).getBody();
//				if (res != null) {
//					String dataId = res.getString("result");
//					log.info("保存流程草稿 dataId : " + dataId);
//				}
				} catch (Exception e) {
					log.error("保存流程草稿异常, "+e.getMessage(),e);
				}
			}
			//update-end--- author:scott---date:20200313------for:移动OA统一草稿箱功能（online表单+设计器表单）,在保存表单时，判断关联流程的话则往流程中间表插入表单数据------------
		}
	}

	@Override
	public JSONObject queryAllDataByTableNameForDesform(String tableName, String dataIds) throws DBException {
		JSONObject json = new JSONObject();
		// 查询主表的数据
		LambdaQueryWrapper<OnlCgformHead> mainQueryWrapper = new LambdaQueryWrapper<>();
		mainQueryWrapper.eq(OnlCgformHead::getTableName, tableName);
		OnlCgformHead head = super.getOne(mainQueryWrapper);
		if (head == null) {
			throw new JeecgBootException("表单数据不存在！");
		}
		// 查询主表以及子表的数据，但子表的数据无法判断是一对一还是一对多，需要单独查询
		Map<String, Object> allDataMap = this.queryManyFormData(head.getId(), dataIds);
		if (allDataMap == null) {
			throw new JeecgBootException("表单数据查询失败！");
		}
		JSONObject allData = JSON.parseObject(JSON.toJSONString(allDataMap));

		// 判断是否有子表
		String subTableStr = head.getSubTableStr();
		if (StringUtil.isNotBlank(subTableStr)) {
			List<String> subTableNameList = new ArrayList<>(Arrays.asList(subTableStr.split(",")));
			LambdaQueryWrapper<OnlCgformHead> subQueryWrapper = new LambdaQueryWrapper<>();
			subQueryWrapper.in(OnlCgformHead::getTableName, subTableNameList);
			List<OnlCgformHead> subTableList = super.list(subQueryWrapper);
			// 一对多子表数据
			JSONObject one2manyData = new JSONObject();
			// 一对一子表数据
			JSONObject one2oneData = new JSONObject();
			// 遍历子表分类数据
			for (OnlCgformHead subCgform : subTableList) {
				JSONArray subDataList = allData.getJSONArray(subCgform.getTableName());
				if (subDataList != null && subDataList.size() > 0) {
					// 判断是附表类型是一对一还是一对多
					if (0 == subCgform.getRelationType()) {
						one2manyData.put(subCgform.getTableName(), subDataList);
					} else {
						JSONObject subData = subDataList.getJSONObject(0);
						one2oneData.put(subCgform.getTableName(), subData);
					}
				}
				allData.remove(subCgform.getTableName());
			}
			json.put("one2one", one2oneData);
			json.put("one2many", one2manyData);
		}
		json.put("main", allData);
		return json;
	}


	/**
	 * 获取online的数据源对象信息 [online支持动态数据源]
	 * @return
	 */
	private DataBaseConfig getOnlineDataBaseConfig(){
		if(oConvertUtils.isEmpty(onlineDatasource)){
			return dataBaseConfig;
		}else{
			DataSourceProperty db = CommonUtils.getDataSourceProperty(onlineDatasource);
			if(db==null){
				log.error("jeecg.online.datasource配置错误,获取不到数据源返回master");
				return dataBaseConfig;
			}else{
				DataBaseConfig config = new DataBaseConfig();
				config.setDriverClassName(db.getDriverClassName());
				config.setPassword(db.getPassword());
				config.setUsername(db.getUsername());
				config.setUrl(db.getUrl());
				config.setDmDataBaseConfig(new DmDataBaseConfig());
				return config;
			}
		}
	}
}
